PageRenderTime 63ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/documentation/spice_overview.md

https://github.com/a-West/duckduckgo
Markdown | 547 lines | 349 code | 198 blank | 0 comment | 0 complexity | 34d0a6ab3333fdf4c76aa5e20dad2436 MD5 | raw file
  1. # Spice Tutorial
  2. [Index](https://github.com/duckduckgo/duckduckgo#index) / **Spice Tutorial**
  3. ---
  4. Spice plugins are triggered by a backend Perl component that then calls the JSON API of an upstream service. The API response is wrapped in a JavaScript function call. You, the plugin author, define this callback function and handle the API's response on the client side, generating the display from the data returned by the API.
  5. **Important:** This is a pretty long document, but it's broken up into logical sections. Follow along and by the end, you'll have all the knowledge you need for Spice creation.
  6. ## Table of Contents
  7. 1. **[Basic Tutorial](#basic-tutorial)** -- this will show you the fundamentals of making a plugin. It's a simple walkthrough-by-example and gives a good introduction to the system. The plugin that we construct is technically a Goodie, but Goodies are the building-blocks of Spice plugins.
  8. 2. **[Spice Handle Functions](#spice-handle-functions)** -- this section provides an overview of the different variables that a spice plugin can process.
  9. 3. **[Testing Triggers](#testing-triggers)** -- this will lead you through how to use duckpan, our command-line utility, to test the plugins that you've written and make sure your triggers are working properly.
  10. Steps 1-3 complete the back-end portion of the Spice development process. These steps contain all of the relevant information pertaining to the Perl piece of the plugin and teach you how to make the system trigger your plugin. Once you've mastered this process, it's time to move on to the front-end documentation, which shows you how to construct the JavaScript part of your plugin that will run in the browser and set your plugin's style.
  11. **[Spice Frontend](spice2.md)** -- once you have the Perl part of the spice working, you still need to define the JS parts and the style.
  12. After reading the frontend section, you will have completed the development process. All that remains is to [test](#testing-spice) and read the [guidelines for submission](#submitting-plugins). You can also find more advanced plugin information in the following places:
  13. **[Advanced Spice](https://github.com/duckduckgo/zeroclickinfo-spice#advanced-spice)** contains a section with information about more involved Spice creation, specifically regarding Spice backends.
  14. **[Advanced Frontend Techniques](spice2.md#advanced-techniques)** has some good information on more complex frontend design.
  15. **[Advanced General](advanced.md)** contains advanced plugin-agnostic docs.
  16. ---
  17. ## Basic Tutorial
  18. Also in: [Index](https://github.com/duckduckgo/duckduckgo#index) / [General](general.md#general) / **Basic Tutorial**
  19. ---
  20. In this tutorial, we'll be making a Goodie plugin that checks the number of characters in a given search query. The purpose of the tutorial is to show how triggers work, which is also relevant for spice development. The end result will look [like this](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Chars.pm) and works [like this](https://duckduckgo.com/?q=chars+How+many+characters+are+in+this+sentence%3F). The same framework is used to trigger Spice plugins.
  21. Let's begin. Open a text editor like [gedit](http://projects.gnome.org/gedit/), notepad or [emacs](http://www.gnu.org/software/emacs/) and type the following:
  22. ```perl
  23. package DDG::Goodie::Chars;
  24. # ABSTRACT: Give the number of characters (length) of the query.
  25. ```
  26. Each plugin is a [Perl package](https://duckduckgo.com/?q=perl+package), so we start by declaring the package namespace. In a new plugin, you would change **Chars** to the name of the new plugin (written in [CamelCase](https://duckduckgo.com/?q=camelcase) format).
  27. The second line is a special comment line that gets parsed automatically to make nice documentation (by [Dist::Zilla](https://metacpan.org/module/Dist::Zilla)).
  28. Next, type the following [use statement](https://duckduckgo.com/?q=perl+use) to import [the magic behind](https://github.com/duckduckgo/duckduckgo/tree/master/lib/DDG) our plugin system.
  29. ```perl
  30. use DDG::Goodie;
  31. ```
  32. ---
  33. #### A Note on Modules
  34. Right after the above line, you should include any Perl modules that you'll be leveraging to help generate the answer. Make sure you add those modules to the dist.ini file in this repository.
  35. If you're not using any additional modules, carry on!
  36. ----
  37. Now here's where it gets interesting. Type:
  38. ```perl
  39. triggers start => 'chars';
  40. ```
  41. **triggers** are keywords that tell us when to make the plugin run. They are _trigger words_. When a particular trigger word is part of a search query, it tells DuckDuckGo to _trigger_ the appropriate plugins.
  42. In this case there is one trigger word: **chars**. Let's say someone searched "chars this is a test." **chars** is the first word so it would trigger our Goodie. The **start** keyword says, "Make sure the trigger word is at the start of the query." The system has several other keywords like **start** that are enumerated in the [Triggers](general.md#triggers) section. The **=>** symbol is there to separate the trigger words from the keywords (for readability).
  43. Now type in this line:
  44. ```perl
  45. handle remainder => sub {
  46. ```
  47. Once triggers are specified, we define how to _handle_ the query. **handle** is another keyword, similar to triggers.
  48. You can _handle_ different aspects of the search query, but the most common is the **remainder**, which refers to the rest of the query (everything but the triggers). For example, if the query was _"chars this is a test"_, the trigger would be _chars_ and the remainder would be _this is a test_.
  49. Now let's add a few more lines to complete the handle function.
  50. ```perl
  51. handle remainder => sub {
  52. return 'Chars: ' . length $_ if $_;
  53. return;
  54. };
  55. ```
  56. This function (the part within the **{}** after **sub**) is the meat of the Goodie. It generates the instant answer that is displayed at the top of the [search results page](https://duckduckgo.com/?q=chars+this+is+a+test).
  57. Whatever you are handling is passed to the function in the **$_** variable ( **$_** is a special default variable in Perl that is commonly used to store temporary values). For example, if you searched DuckDuckGo for _"chars this is a test"_, the value of **$_** will be _"this is a test"_, i.e. the remainder.
  58. Let's take a closer look at the first line of the function.
  59. ```perl
  60. return 'Chars: ' . length $_ if $_;
  61. ```
  62. The heart of the function is just this one line. The **remainder** is in the **$_** variable as discussed. If it is not blank ( **if $_** ), we return the number of chars using Perl's built-in [length function](https://duckduckgo.com/?q=perl+length).
  63. Perl has a lot of built-in functions, as well as thousands and thousands of modules available [via CPAN](https://metacpan.org/). You can leverage these modules when making Goodies, similar to how the [Roman Goodie](https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Roman.pm) uses the [Roman module](https://metacpan.org/module/Roman).
  64. If we are unable to provide a good instant answer, we simply **return** nothing. And that's exactly what the second line in the function does.
  65. ```perl
  66. return;
  67. ```
  68. This line is only run if **$_** contained nothing, because otherwise the line before it would return something and end the function.
  69. Now, below your function type the following line:
  70. ```perl
  71. zci is_cached => 1;
  72. ```
  73. This line is optional. We set **is_cached** to true (0 is false, 1 is true) because this plugin will always return the same answer for the same query. This speeds up future answers by caching them (saving previous answers).
  74. Finally, all Perl packages that load correctly should [return a true value](http://stackoverflow.com/questions/5293246/why-the-1-at-the-end-of-each-perl-package) so add a 1 on the very last line.
  75. ```perl
  76. 1;
  77. ```
  78. And that's it! At this point you have a working DuckDuckHack Goodie plugin. It should look like this:
  79. ```perl
  80. package DDG::Goodie::Chars;
  81. # ABSTRACT: Give the number of characters (length) of the query.
  82. use DDG::Goodie;
  83. triggers start => 'chars';
  84. handle remainder => sub {
  85. return 'Chars: ' . length $_ if $_;
  86. return;
  87. };
  88. zci is_cached => 1;
  89. 1;
  90. ```
  91. ### Review
  92. The plugin system works like this at the highest level:
  93. * We break the query (search terms) into words. This process happens in the background.
  94. * We see if any of those words are **triggers** (trigger words). These are provided by each of the plugins. In the example, the trigger word is **chars**.
  95. * If a Goodie plugin is triggered, we run its **handle** function.
  96. * If the Goodie's handle function outputs an instant answer via a **return** statement, we pass it back to the user.
  97. **Now**: continue down to Spice Handle Functions, or go back to the [Table of Contents](#table-of-contents).
  98. ***
  99. ## Spice Handle Functions
  100. Also in: [Index](https://github.com/duckduckgo/duckduckgo#index) / [Spice](spice.md#spice) / **Spice Handle Functions**
  101. ---
  102. Spice plugins have **triggers** and **handle** functions like Goodies, as explained above in the [Basic tutorial](#basic-tutorial). The difference is that Spice handle functions don't return an instant answer directly like Goodies. Instead, they return arguments used to call an external API, which then calls a JavaScript callback function that finally returns the instant answer.
  103. The JavaScript callback function is defined in another file and is explained in detail in the [Frontend documentation](spice2.md) section. For now let's concentrate on how it gets called via the Spice handle function.
  104. Usually the Spice plugin flow works like this:
  105. * Spice plugin is triggered.
  106. * Spice handle function is called.
  107. * Spice handle function returns arguments.
  108. * Arguments are used to make a call to an external [JSONP](https://duckduckgo.com/?q=jsonp) API.
  109. * The external API returns a [JSON](https://duckduckgo.com/?q=JSON) object to the Spice callback function.
  110. * Spice callback function returns instant answer.
  111. * Instant answer formatted on screen.
  112. The following is [an example](https://duckduckgo.com/?q=npm+uglify-js) that calls [the Node.js package search API](http://registry.npmjs.org/uglify-js/latest). Within your **zeroclickinfo-spice** fork, you would define a similar file in the **/lib/DDG/Spice/** directory. This file is named **Npm.pm**.
  113. ```perl
  114. package DDG::Spice::Npm;
  115. use DDG::Spice;
  116. spice to => 'http://registry.npmjs.org/$1/latest';
  117. spice wrap_jsonp_callback => 1;
  118. spice is_cached => 0;
  119. triggers startend => 'npm';
  120. handle remainder => sub {
  121. return $_ if $_;
  122. return;
  123. };
  124. 1;
  125. ```
  126. To refresh your memory, the **triggers** keyword tells the plugin system when to call a plugin. In the [Basic Tutorial](#basic-tutorial) we discussed using the **start** keyword to specify trigger words that need to be present at the beginning of the query. Check out the section on [Triggers](general.md#triggers) for more information.
  127. Previously we saw the use of the **remainder** keyword as in **handle remainder**, which works well for trigger words. We use it again here.
  128. ```perl
  129. return $_ if $_;
  130. ```
  131. `$_` is a contextual variable in Perl. Its value in this case is the **remainder**. That is, `uglify-js` in the linked example above -- the query with the trigger word stripped out. If we get a non-blank package name, we return it.
  132. Otherwise, we return nothing, which short-circuits the eventual external call.
  133. ```perl
  134. return;
  135. ```
  136. When the package name is returned we then plug it into the **spice to** definition.
  137. ```perl
  138. spice to => 'http://registry.npmjs.org/$1/latest';
  139. ```
  140. The **$_** value from the return statement will get inserted into the **$1** placeholder in the **spice to** line such that you can plug in parameters to the API call as needed. For passing multiple parameters, check out the [Advanced spice handlers](https://github.com/duckduckgo/zeroclickinfo-spice#advanced-spice-handlers) section.
  141. In this particular case, the API we're using to search for packages does not support callback functions. That is, it will not wrap its response in a function call, which in this case would be **ddg_spice_npm**. If it did support a callback, we could use the **{{callback}}** template in the **spice to** line to automatically fill in the default callback value. See the [IMdB spice](https://github.com/duckduckgo/zeroclickinfo-spice/blob/master/lib/DDG/Spice/Imdb.pm) for a simple implementation of the **{{callback}}** template. Since this API doesn't support the callback parameter, we tell the backend to automatically wrap the API's response in a function call for us with:
  142. ```perl
  143. spice wrap_jsonp_callback => 1;
  144. ```
  145. At this point the response moves from the backend to the frontend. The external API sends a JSON object to the callback function that you will also define, as explained in the following sections. However, before starting work on the front end, we first have to test the plugin triggers to ensure that they work.
  146. **Now:** go on Testing Triggers, or go back to the [Table of Contents](#table-of-contents).
  147. ***
  148. ## Testing Triggers
  149. Also in: [Index](https://github.com/duckduckgo/duckduckgo#index) / [Testing](testing.md#testing) / **Testing Triggers**
  150. ---
  151. **Step 1.**  Install our DuckDuckHack utility called [duckpan](https://metacpan.org/module/App::DuckPAN):
  152. **Warning:** Installing duckpan can take a long time (up to 1 hour on some systems), and Linux is the only offical supported OS. It's possible to get duckpan to work on Mac OS X with a bit of perseverance, but Linux systems are preferrable. Get in touch (see [FAQ](faq.md)) for help with installing duckpan on a Mac.
  153. ```bash
  154. curl http://duckpan.org/install.pl | perl
  155. ```
  156. [This script](https://github.com/duckduckgo/p5-duckpan-installer) will setup [local::lib](https://metacpan.org/module/local::lib), which is a way to install Perl modules without changing your base Perl installation. (If you already use local::lib or [perlbrew](https://metacpan.org/module/perlbrew), don't worry, this script will intelligently use what you already have).
  157. If you didn't have a local::lib before running the install script, you will need to run the script twice. It should tell you when like this:
  158. ```txt
  159. please now re-login to your user account and run it again!
  160. ```
  161. If everything works, you should see this at the end:
  162. ```bash
  163. EVERYTHING OK! You can now go hacking! :)
  164. ```
  165. Note that with local::lib now installed, you can easily install [Perl modules](http://search.cpan.org/) with [cpanm](https://metacpan.org/module/cpanm).
  166. ```bash
  167. cpanm App::DuckPAN
  168. App::DuckPAN is up to date.
  169. ```
  170. **Step 2.**  Go to your fork of the repository (a directory or folder on your computer).
  171. ```bash
  172. cd zeroclickinfo-goodies/
  173. ```
  174. **Step 3.**  Install the repository requirements using duckpan.
  175. ```txt
  176. duckpan installdeps
  177. ```
  178. This command will install all the Perl modules used by the DuckDuckGo plugins within your local repository. These requirements are defined in the [/dist.ini file](http://blog.urth.org/2010/06/walking-through-a-real-distini.html) (at the root). **Warning:** this also may take a while.
  179. **Step 4.** Add your plugin.
  180. Make a new file in the **lib/DDG/Goodie/** directory for Goodies or the **lib/DDG/Spice/** directory for Spice. The name of the file is the name of the plugin followed by the extension **.pm** because it is a Perl package. For example, if the name of your plugin was _TestPlugin_, the file would be _TestPlugin.pm_.
  181. **Step 5.** Test your trigger(s) interactively.
  182. Type this command at the command line.
  183. ```txt
  184. duckpan query
  185. ```
  186. First, this command will output all of the plugins available in your local plugin repository.
  187. ```md
  188. Using the following DDG::Goodie plugins:
  189. - DDG::Goodie::Xor (Words)
  190. - DDG::Goodie::SigFigs (Words)
  191. - DDG::Goodie::EmToPx (Words)
  192. - DDG::Goodie::Length (Words)
  193. - DDG::Goodie::ABC (Words)
  194. - DDG::Goodie::Chars (Words)
  195. ...
  196. ```
  197. You should see your plugin in there as well. When the output is finished it gives you an interactive prompt.
  198. ```
  199. (Empty query for ending test)
  200. Query:
  201. ```
  202. Now you can type in any query and see what the response will be.
  203. ```
  204. Query: chars this is a test
  205. DDG::ZeroClickInfo {
  206. Parents WWW::DuckDuckGo::ZeroClickInfo
  207. Linear @ISA DDG::ZeroClickInfo, WWW::DuckDuckGo::ZeroClickInfo, Moo::Object
  208. public methods (3) : is_cached, new, ttl
  209. private methods (0)
  210. internals: {
  211. answer 14,
  212. answer_type "chars",
  213. is_cached 1
  214. }
  215. }
  216. ```
  217. There is a lot of debugging output, but you will want to pay special attention to the internals section.
  218. ```txt
  219. internals: {
  220. answer 14,
  221. answer_type "chars",
  222. is_cached 1
  223. }
  224. ```
  225. Here you can see the answer returned, as well as any **zci** keywords (by default there will be a default **answer_type** and **is_cached** value).
  226. Simply hit enter (a blank query) to exit interactive mode.
  227. ```txt
  228. Query:
  229. \_o< Thanks for testing!
  230. ```
  231. **Now:** go on to [Spice Frontend](#spice-frontend), or go back to the [Table of Contents](#table-of-contents).
  232. ***
  233. ## Spice Frontend
  234. Also in: [Index](https://github.com/duckduckgo/duckduckgo#index) / [Spice](spice.md#spice) / **Spice Frontend**
  235. ---
  236. **Note**: The Perl part of the plugins go in lib directory: `lib/DDG/Spice/PluginName.pm`, while all of the frontend files discussed below should go in the share directory: `share/spice/plugin_name/`.
  237. The NPM plugin [[link](https://duckduckgo.com/?q=npm+uglify-js)] [[code](https://github.com/duckduckgo/zeroclickinfo-spice/tree/master/share/spice/npm)] is a great example of a basic Spice implementation. Let's walk through it line-by-line:
  238. #####npm.js
  239. ```javascript
  240. function ddg_spice_npm (api_result) {
  241. if (api_result.error) return
  242. Spice.render({
  243. data : api_result,
  244. force_big_header : true,
  245. header1 : api_result.name + ' (' + api_result.version + ')',
  246. source_name : "npmjs.org", // More at ...
  247. source_url : 'http://npmjs.org/package/' + api_result.name,
  248. template_normal : 'npm',
  249. template_small : 'npm'
  250. });
  251. }
  252. ```
  253. As mentioned, every plugin requires a Spice callback function, for the *NPM* plugin, the callback is the `ddg_spice_npm()` function that we defined here in *npm.js*. The *NPM* Perl module we wrote specifies this as the callback by using the name of the package `DDG::Spice::NPM` and gives this *ddg_spice_npm* name to the API call (well, in this particular case, tells the Perl backend to wrap the API response in the correct function call since the API doesn't support callbacks) so that this funtion will be executed when the API responds using the data returned from the upstream (API) provider as the function's input.
  254. #####npm.js (continued)
  255. ```javascript
  256. if (api_result.error) return
  257. ```
  258. Pretty self-explanatory - If the error object in the API result is defined, then break out of the function and don't show any results. In the case of this API, when the error object is defined, it means no results are given, so we have no data to use for a Spice result. Note that ```api_result.error``` is not a generalized object property and is specific to this API. Other APIs may have different names for their error responses.
  259. #####npm.js (continued)
  260. ```javascript
  261. Spice.render({
  262. data : api_result,
  263. force_big_header : true,
  264. header1 : api_result.name + ' (' + api_result.version + ')',
  265. source_name : "npmjs.org",
  266. source_url : 'http://npmjs.org/package/' + api_result.name,
  267. template_normal : 'npm',
  268. template_small : 'npm'
  269. });
  270. ```
  271. Alright, so here is the bulk of the plugin, but it's very simple:
  272. - `Spice.render()` is a function that the plugin system has already defined. You pass an object to it that specifies a bunch of important parameters.
  273. - `data` is perhaps the most important parameter. The object given here will be the object that is passed along to the Handlebars template. In this case, the context of the NPM template will be the **api_result** object. This is very important to understand because **only the data passed along to the template is accessible to the template**. In most cases the `data` parameter should be set to
  274. `api_result` so all the data returned from the API is accessible to the template.
  275. - `force_big_header` is related to the display formatting -- it forces the system to display the large grey header that you see when you click the example link above.
  276. - `header1` is the text of this header, i.e. the text displayed inside the large grey bar.
  277. - `source_name` is the name of the source for the "More at <source>" link that's displayed below the text of the plugin for attribution purposes.
  278. - `source_url` is the target of the "More at" link. It's the page that the user will click through to.
  279. - `template_normal` is the name of the Handlebars template that contains the structure information for your plugin.
  280. - `template_small` is the name of the Handlebars template to be used when you plugin is displayed in a stacked state. This isn't required, but if your plugin can provide a succint, one or two line answer this template should be used in the event that the plugin appears in the stacked state. If no template is given the stacked result will simply show the header of the spice result
  281. ----
  282. Now, let's look at the NPM plugin's Handlebars template:
  283. ######npm.handlebars
  284. ```html
  285. <div>
  286. <div>{{{description}}}</div>
  287. <pre> $ npm install {{{name}}}</pre>
  288. </div>
  289. ```
  290. As you can see, this is a special type of HTML template. Within the template, you can refer directly to objects that are returned by the API. `description` and `name` are both from the `api_result` object that we discussed earlier -- the data that's returned by the API. All of `api_result`'s sub-objects (e.g. `name`, `description`) are in the template's scope. You can access them by name using double or triple curly braces (triple braces will not escape the contents). Here, we just create a basic HTML skeleton and fill it in with the proper information.
  291. ###Conclusion
  292. We've created two files in the Spice share directory (`share/spice/npm/`) :
  293. 1. `npm.js` - which delegates the API's response and calls `Spice.render()`
  294. 2. `npm.handlebars` - which specifies the plugin's HTML structure and determines which attributes of the API response are placed in the HTML result
  295. You may notice other plugins also include a css file. For **NPM** the use of CSS wasn't necessary and this is also true for many other plugins. If however CSS is needed it can be added. Please refer to the [Frontend FAQ](spice2.md#faq) for more inforamtion about custom css.
  296. You can find plenty more examples of Spice frontend code in the dedicated document on frontend development, found [here](spice2.md).
  297. **Now:** go on Testing Spice, or go back to the [Table of Contents](#table-of-contents).
  298. ## Testing Spice
  299. Also in: [Index](https://github.com/duckduckgo/duckduckgo#index) / [Testing](testing.md#testing) / **Testing Spice**
  300. ---
  301. You should have already tested your Spice triggers by following the [Testing triggers](#testing-triggers) section. Once you're confident your triggers are functioning properly, follow these steps to see your Spice plugin on a live server!
  302. **Step 1.** &nbsp;Go to the roof of your forked repository.
  303. ```bash
  304. cd zeroclickinfo-spice/
  305. ```
  306. **Step 2.** &nbsp;Start the server.
  307. ```bash
  308. duckpan server
  309. ```
  310. This command will start up a small Web server running on port 5000 on your machine.
  311. **Step 3.** &nbsp;Visit the server in your browser.
  312. You should now be able to go to your duckpan server via a regular Web browser and check it out. It runs code from our site and so generally looks like a real version of DuckDuckGo.
  313. If you're running the duckpan server on the same computer as your Web browser you can navigate to:
  314. ```bash
  315. http://127.0.0.1:5000/
  316. ```
  317. If you're running the duckpan server on a remote machine, then substitute 127.0.0.1 wither either its IP address or its Fully Qualified Domain Name.
  318. **Step 4.** &nbsp;Search.
  319. Given you've already tested your plugin triggers, you should be able to search and see your spice output come through the server. As requests go through the internal Web server they are printed to STDOUT (on the screen). External API calls are highlighted (if you have color turned on in your terminal).
  320. **Step 5.** &nbsp;Debug.
  321. If for some reason a search doesn't hit a plugin, there is an error message displayed on the homepage saying "Sorry, no hit for your plugins."
  322. If it does hit and you do not see something displayed on the screen, a number of things could be going wrong.
  323. * You have a JavaScript error of some kind. Check out the JavaScript console for details. Personally we like to use [Firebug](http://getfirebug.com/) internally.
  324. * The external API was not called correctly. You should be able to examine the Web server output to make sure the right API is being called. If it's not you will need to revise your [Spice handle function](#spice-handle-functions).
  325. * The external API did not return anything. Firebug is great for checking this as well. You should see the call in your browser and then you can examine the response.
  326. **Step 6.** &nbsp;Tweak the display.
  327. Once everything is working properly (and you have stuff displayed on screen), you will want to mess with your callback function to get the display nice and perfect. Check out the [Guidelines](getting_started.md#guidelines) for some pointers.
  328. **Step 7.** &nbsp;Document.
  329. Finally, please document as much as possible using in-line comments.
  330. **Now:** You're almost done! Read up on how to submit plugins, or go back to the [Table of Contents](#table-of-content).
  331. ## Submitting Plugins
  332. Also in: [Index](https://github.com/duckduckgo/duckduckgo#index) / [General](general.md#general) / **Submitting Plugins**
  333. ---
  334. **Step 1.** &nbsp;Commit your changes.
  335. ```bash
  336. git commit -a -m "My first plugin that does X is ready to go!"
  337. ```
  338. **Step 2.** &nbsp;Get your commit history [how you like it](http://book.git-scm.com/4_interactive_rebasing.html).
  339. ```
  340. git rebase -i origin/master
  341. ```
  342. **Step 3.** &nbsp;Push your forked repository back to GitHub.
  343. ```
  344. git push
  345. ```
  346. **Step 4.** Add your info to the plugin so we can give you credit for it on the [Goodies page](https://duckduckgo.com/goodies). You'll see your name or handle on the live site!
  347. Check out the [Metadata README](metadata.md) for detailed instructions on how to include your name and links.
  348. **Step 5.** &nbsp;Go into GitHub and submit a [pull request!](http://help.github.com/send-pull-requests/) That will let us know about your plugin and start the conversation about integrating it into the live search engine.
  349. **Now:** If you're interested, go back to the [Table of Contents](#table-of-contents) and check out the Advanced links for more information. Otherwise, get hacking!