CINXE.COM

Developer Documentation

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="generator" content="Asciidoctor 2.0.18"> <title>Developer Documentation</title> <style> /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /*! normalize.css v2.1.2 | MIT License | git.io/normalize */ /* ========================================================================== HTML5 display definitions ========================================================================== */ /** Correct `block` display not defined in IE 8/9. */ @import url(http://cdnjs.cloudflare.com/ajax/libs/font-awesome/3.2.1/css/font-awesome.css); article, aside, details, figcaption, figure, footer, header, hgroup, main, nav, section, summary { display: block; } /** Correct `inline-block` display not defined in IE 8/9. */ audio, canvas, video { display: inline-block; } /** Prevent modern browsers from displaying `audio` without controls. Remove excess height in iOS 5 devices. */ audio:not([controls]) { display: none; height: 0; } /** Address `[hidden]` styling not present in IE 8/9. Hide the `template` element in IE, Safari, and Firefox < 22. */ [hidden], template { display: none; } script { display: none !important; } /* ========================================================================== Base ========================================================================== */ /** 1. Set default font family to sans-serif. 2. Prevent iOS text size adjust after orientation change, without disabling user zoom. */ html { font-family: sans-serif; /* 1 */ -ms-text-size-adjust: 100%; /* 2 */ -webkit-text-size-adjust: 100%; /* 2 */ } /** Remove default margin. */ body { margin: 0; } /* ========================================================================== Links ========================================================================== */ /** Remove the gray background color from active links in IE 10. */ a { background: transparent; } /** Address `outline` inconsistency between Chrome and other browsers. */ a:focus { outline: thin dotted; } /** Improve readability when focused and also mouse hovered in all browsers. */ a:active, a:hover { outline: 0; } /* ========================================================================== Typography ========================================================================== */ /** Address variable `h1` font-size and margin within `section` and `article` contexts in Firefox 4+, Safari 5, and Chrome. */ h1 { font-size: 2em; margin: 0.67em 0; } /** Address styling not present in IE 8/9, Safari 5, and Chrome. */ abbr[title] { border-bottom: 1px dotted; } /** Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome. */ b, strong { font-weight: bold; } /** Address styling not present in Safari 5 and Chrome. */ dfn { font-style: italic; } /** Address differences between Firefox and other browsers. */ hr { -moz-box-sizing: content-box; box-sizing: content-box; height: 0; } /** Address styling not present in IE 8/9. */ mark { background: #ff0; color: #000; } /** Correct font family set oddly in Safari 5 and Chrome. */ code, kbd, pre, samp { font-family: monospace, serif; font-size: 1em; } /** Improve readability of pre-formatted text in all browsers. */ pre { white-space: pre-wrap; } /** Set consistent quote types. */ q { quotes: "\201C" "\201D" "\2018" "\2019"; } /** Address inconsistent and variable font size in all browsers. */ small { font-size: 80%; } /** Prevent `sub` and `sup` affecting `line-height` in all browsers. */ sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; } sup { top: -0.5em; } sub { bottom: -0.25em; } /* ========================================================================== Embedded content ========================================================================== */ /** Remove border when inside `a` element in IE 8/9. */ img { border: 0; } /** Correct overflow displayed oddly in IE 9. */ svg:not(:root) { overflow: hidden; } /* ========================================================================== Figures ========================================================================== */ /** Address margin not present in IE 8/9 and Safari 5. */ figure { margin: 0; } /* ========================================================================== Forms ========================================================================== */ /** Define consistent border, margin, and padding. */ fieldset { border: 1px solid #c0c0c0; margin: 0 2px; padding: 0.35em 0.625em 0.75em; } /** 1. Correct `color` not being inherited in IE 8/9. 2. Remove padding so people aren't caught out if they zero out fieldsets. */ legend { border: 0; /* 1 */ padding: 0; /* 2 */ } /** 1. Correct font family not being inherited in all browsers. 2. Correct font size not being inherited in all browsers. 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome. */ button, input, select, textarea { font-family: inherit; /* 1 */ font-size: 100%; /* 2 */ margin: 0; /* 3 */ } /** Address Firefox 4+ setting `line-height` on `input` using `!important` in the UA stylesheet. */ button, input { line-height: normal; } /** Address inconsistent `text-transform` inheritance for `button` and `select`. All other form control elements do not inherit `text-transform` values. Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+. Correct `select` style inheritance in Firefox 4+ and Opera. */ button, select { text-transform: none; } /** 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` and `video` controls. 2. Correct inability to style clickable `input` types in iOS. 3. Improve usability and consistency of cursor style between image-type `input` and others. */ button, html input[type="button"], input[type="reset"], input[type="submit"] { -webkit-appearance: button; /* 2 */ cursor: pointer; /* 3 */ } /** Re-set default cursor for disabled elements. */ button[disabled], html input[disabled] { cursor: default; } /** 1. Address box sizing set to `content-box` in IE 8/9. 2. Remove excess padding in IE 8/9. */ input[type="checkbox"], input[type="radio"] { box-sizing: border-box; /* 1 */ padding: 0; /* 2 */ } /** 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome (include `-moz` to future-proof). */ input[type="search"] { -webkit-appearance: textfield; /* 1 */ -moz-box-sizing: content-box; -webkit-box-sizing: content-box; /* 2 */ box-sizing: content-box; } /** Remove inner padding and search cancel button in Safari 5 and Chrome on OS X. */ input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; } /** Remove inner padding and border in Firefox 4+. */ button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; } /** 1. Remove default vertical scrollbar in IE 8/9. 2. Improve readability and alignment in all browsers. */ textarea { overflow: auto; /* 1 */ vertical-align: top; /* 2 */ } /* ========================================================================== Tables ========================================================================== */ /** Remove most spacing between table cells. */ table { border-collapse: collapse; border-spacing: 0; } meta.foundation-mq-small { font-family: "only screen and (min-width: 768px)"; width: 768px; } meta.foundation-mq-medium { font-family: "only screen and (min-width:1280px)"; width: 1280px; } meta.foundation-mq-large { font-family: "only screen and (min-width:1440px)"; width: 1440px; } *, *:before, *:after { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; } html, body { font-size: 100%; } body { background: white; color: #222222; padding: 0; margin: 0; font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; font-weight: normal; font-style: normal; line-height: 1; position: relative; cursor: auto; } a:hover { cursor: pointer; } img, object, embed { max-width: 100%; height: auto; } object, embed { height: 100%; } img { -ms-interpolation-mode: bicubic; } #map_canvas img, #map_canvas embed, #map_canvas object, .map_canvas img, .map_canvas embed, .map_canvas object { max-width: none !important; } .left { float: left !important; } .right { float: right !important; } .text-left { text-align: left !important; } .text-right { text-align: right !important; } .text-center { text-align: center !important; } .text-justify { text-align: justify !important; } .hide { display: none; } .antialiased, body { -webkit-font-smoothing: antialiased; } img { display: inline-block; vertical-align: middle; } textarea { height: auto; min-height: 50px; } select { width: 100%; } p.lead, .paragraph.lead > p, #preamble > .sectionbody > .paragraph:first-of-type p { font-size: 1.21875em; line-height: 1.6; } .subheader, #content #toctitle, .admonitionblock td.content > .title, .exampleblock > .title, .imageblock > .title, .listingblock > .title, .literalblock > .title, .mathblock > .title, .openblock > .title, .paragraph > .title, .quoteblock > .title, .sidebarblock > .title, .tableblock > .title, .verseblock > .title, .videoblock > .title, .dlist > .title, .olist > .title, .ulist > .title, .qlist > .title, .hdlist > .title, .tableblock > caption { line-height: 1.4; color: #6c818f; font-weight: 300; margin-top: 0.2em; margin-bottom: 0.5em; } /* Typography resets */ div, dl, dt, dd, ul, ol, li, h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6, pre, form, p, blockquote, th, td { margin: 0; padding: 0; direction: ltr; } /* Default Link Styles */ a { color: #444444; text-decoration: underline; line-height: inherit; } a:hover, a:focus { color: #111111; } a img { border: none; } /* Default paragraph styles */ p { font-family: inherit; font-weight: normal; font-size: 1em; line-height: 1.5; margin-bottom: 1.25em; text-rendering: optimizeLegibility; } p aside { font-size: 0.875em; line-height: 1.35; font-style: italic; } /* Default header styles */ h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 { font-family: ff-meta-web-pro-1, ff-meta-web-pro-2, Arial, "Helvetica Neue", sans-serif; font-weight: bold; font-style: normal; color: #465158; text-rendering: optimizeLegibility; margin-top: 1em; margin-bottom: 0.5em; line-height: 1.2125em; } h1 small, h2 small, h3 small, #toctitle small, .sidebarblock > .content > .title small, h4 small, h5 small, h6 small { font-size: 60%; color: #909ea7; line-height: 0; } h1 { font-size: 2.125em; } h2 { font-size: 1.6875em; } h3, #toctitle, .sidebarblock > .content > .title { font-size: 1.375em; } h4 { font-size: 1.125em; } h5 { font-size: 1.125em; } h6 { font-size: 1em; } hr { border: solid #dddddd; border-width: 1px 0 0; clear: both; margin: 1.25em 0 1.1875em; height: 0; } /* Helpful Typography Defaults */ em, i { font-style: italic; line-height: inherit; } strong, b { font-weight: bold; line-height: inherit; } small { font-size: 60%; line-height: inherit; } code { font-family: "Consolas", "Deja Vu Sans Mono", "Bitstream Vera Sans Mono", monospace; font-weight: normal; color: #444444; } /* Lists */ ul, ol, dl { font-size: 1em; line-height: 1.5; margin-bottom: 1.25em; list-style-position: outside; font-family: inherit; } ul, ol { margin-left: 0; } ul.no-bullet, ol.no-bullet { margin-left: 0; } /* Unordered Lists */ ul li ul, ul li ol { margin-left: 1.25em; margin-bottom: 0; font-size: 1em; /* Override nested font-size change */ } ul.square li ul, ul.circle li ul, ul.disc li ul { list-style: inherit; } ul.square { list-style-type: square; } ul.circle { list-style-type: circle; } ul.disc { list-style-type: disc; } ul.no-bullet { list-style: none; } /* Ordered Lists */ ol li ul, ol li ol { margin-left: 1.25em; margin-bottom: 0; } /* Definition Lists */ dl dt { margin-bottom: 0.3em; font-weight: bold; } dl dd { margin-bottom: 0.75em; } /* Abbreviations */ abbr, acronym { text-transform: uppercase; font-size: 90%; color: black; border-bottom: 1px dotted #dddddd; cursor: help; } abbr { text-transform: none; } /* Blockquotes */ blockquote { margin: 0 0 1.25em; padding: 0.5625em 1.25em 0 1.1875em; border-left: 1px solid #dddddd; } blockquote cite { display: block; font-size: 0.8125em; color: #748590; } blockquote cite:before { content: "\2014 \0020"; } blockquote cite a, blockquote cite a:visited { color: #748590; } blockquote, blockquote p { line-height: 1.5; color: #909ea7; } /* Microformats */ .vcard { display: inline-block; margin: 0 0 1.25em 0; border: 1px solid #dddddd; padding: 0.625em 0.75em; } .vcard li { margin: 0; display: block; } .vcard .fn { font-weight: bold; font-size: 0.9375em; } .vevent .summary { font-weight: bold; } .vevent abbr { cursor: auto; text-decoration: none; font-weight: bold; border: none; padding: 0 0.0625em; } @media only screen and (min-width: 768px) { h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 { line-height: 1.4; } h1 { font-size: 2.75em; } h2 { font-size: 2.3125em; } h3, #toctitle, .sidebarblock > .content > .title { font-size: 1.6875em; } h4 { font-size: 1.4375em; } } /* Print styles. Inlined to avoid required HTTP connection: www.phpied.com/delay-loading-your-print-css/ Credit to Paul Irish and HTML5 Boilerplate (html5boilerplate.com) */ .print-only { display: none !important; } @media print { * { background: transparent !important; color: #000 !important; /* Black prints faster: h5bp.com/s */ box-shadow: none !important; text-shadow: none !important; } a, a:visited { text-decoration: underline; } a[href]:after { content: " (" attr(href) ")"; } abbr[title]:after { content: " (" attr(title) ")"; } .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; } pre, blockquote { border: 1px solid #999; page-break-inside: avoid; } thead { display: table-header-group; /* h5bp.com/t */ } tr, img { page-break-inside: avoid; } img { max-width: 100% !important; } @page { margin: 0.5cm; } p, h2, h3, #toctitle, .sidebarblock > .content > .title { orphans: 3; widows: 3; } h2, h3, #toctitle, .sidebarblock > .content > .title { page-break-after: avoid; } .hide-on-print { display: none !important; } .print-only { display: block !important; } .hide-for-print { display: none !important; } .show-for-print { display: inherit !important; } } /* Tables */ table { background: white; margin-bottom: 1.25em; border: solid 0 #dddddd; } table thead, table tfoot { background: none; font-weight: bold; } table thead tr th, table thead tr td, table tfoot tr th, table tfoot tr td { padding: 1px 8px 1px 5px; font-size: 1em; color: #222222; text-align: left; } table tr th, table tr td { padding: 1px 8px 1px 5px; font-size: 1em; color: #222222; } table tr.even, table tr.alt, table tr:nth-of-type(even) { background: none; } table thead tr th, table tfoot tr th, table tbody tr td, table tr td, table tfoot tr td { display: table-cell; line-height: 1.5; } .clearfix:before, .clearfix:after, .float-group:before, .float-group:after { content: " "; display: table; } .clearfix:after, .float-group:after { clear: both; } *:not(pre) > code { font-size: 0.95em; padding: 0; white-space: nowrap; background-color: #f2f2f2; border: 0 solid #dddddd; -webkit-border-radius: 6px; border-radius: 6px; text-shadow: none; } pre, pre > code { line-height: 1.2; color: inherit; font-family: "Consolas", "Deja Vu Sans Mono", "Bitstream Vera Sans Mono", monospace; font-weight: normal; } .keyseq { color: #333333; } kbd:not(.keyseq) { display: inline-block; color: black; font-size: 0.75em; line-height: 1.4; background-color: #F7F7F7; border: 1px solid #ccc; -webkit-border-radius: 3px; border-radius: 3px; -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), 0 0 0 2px white inset; box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), 0 0 0 2px white inset; margin: -0.15em 0.15em 0 0.15em; padding: 0.2em 0.6em 0.2em 0.5em; vertical-align: middle; white-space: nowrap; } .keyseq kbd:first-child { margin-left: 0; } .keyseq kbd:last-child { margin-right: 0; } .menuseq, .menu { color: black; } b.button:before, b.button:after { position: relative; top: -1px; font-weight: normal; } b.button:before { content: "["; padding: 0 3px 0 2px; } b.button:after { content: "]"; padding: 0 2px 0 3px; } p a > code:hover { color: #373737; } #header, #content, #footnotes, #footer { width: 100%; margin-left: auto; margin-right: auto; margin-top: 0; margin-bottom: 0; max-width: 62.5em; *zoom: 1; position: relative; padding-left: 0.9375em; padding-right: 0.9375em; } #header:before, #header:after, #content:before, #content:after, #footnotes:before, #footnotes:after, #footer:before, #footer:after { content: " "; display: table; } #header:after, #content:after, #footnotes:after, #footer:after { clear: both; } #header { margin-bottom: 2.5em; } #header > h1 { color: #111111; font-weight: bold; border-bottom: 1px solid #dddddd; margin-bottom: -28px; padding-bottom: 32px; } #header span { color: #909ea7; } #header #revnumber { text-transform: capitalize; } #header br { display: none; } #header br + span { padding-left: 3px; } #header br + span:before { content: "\2013 \0020"; } #header br + span.author { padding-left: 0; } #header br + span.author:before { content: ", "; } #toc { border-bottom: 1px solid #dddddd; padding-bottom: 1.25em; } #toc > ul { margin-left: 0.25em; } #toc ul.sectlevel0 > li > a { font-style: italic; } #toc ul.sectlevel0 ul.sectlevel1 { margin-left: 0; margin-top: 0.5em; margin-bottom: 0.5em; } #toc ul { list-style-type: none; } #toctitle { color: #6c818f; } @media only screen and (min-width: 768px) { body.toc2 { padding-left: 15em; padding-right: 0; } #toc.toc2 { position: fixed; width: 15em; left: 0; top: 0; border-right: 1px solid #dddddd; border-bottom: 0; z-index: 1000; padding: 1em; height: 100%; overflow: auto; } #toc.toc2 #toctitle { margin-top: 0; font-size: 1.2em; } #toc.toc2 > ul { font-size: .90em; } #toc.toc2 ul ul { margin-left: 0; padding-left: 1em; } #toc.toc2 ul.sectlevel0 ul.sectlevel1 { padding-left: 0; margin-top: 0.5em; margin-bottom: 0.5em; } body.toc2.toc-right { padding-left: 0; padding-right: 15em; } body.toc2.toc-right #toc.toc2 { border-right: 0; border-left: 1px solid #dddddd; left: auto; right: 0; } } @media only screen and (min-width: 1280px) { body.toc2 { padding-left: 20em; padding-right: 0; } #toc.toc2 { width: 20em; } #toc.toc2 #toctitle { font-size: 1.375em; } #toc.toc2 > ul { font-size: 0.95em; } #toc.toc2 ul ul { padding-left: 1.25em; } body.toc2.toc-right { padding-left: 0; padding-right: 20em; } } #content #toc { border-style: solid; border-width: 1px; border-color: #d9d9d9; margin-bottom: 1.25em; padding: 1.25em; background: #f2f2f2; border-width: 0; -webkit-border-radius: 6px; border-radius: 6px; } #content #toc > :first-child { margin-top: 0; } #content #toc > :last-child { margin-bottom: 0; } #content #toc a { text-decoration: none; } #content #toctitle { font-weight: bold; font-family: ff-meta-web-pro-1, ff-meta-web-pro-2, Arial, "Helvetica Neue", sans-serif; font-size: 1em; padding-left: 0.125em; } #footer { max-width: 100%; background-color: black; padding: 1.25em; } #footer-text { color: white; line-height: 1.35; } .sect1 { padding-bottom: 1.25em; } .sect1 + .sect1 { border-top: 1px solid #dddddd; } #content h1 > a.anchor, h2 > a.anchor, h3 > a.anchor, #toctitle > a.anchor, .sidebarblock > .content > .title > a.anchor, h4 > a.anchor, h5 > a.anchor, h6 > a.anchor { position: absolute; width: 1em; margin-left: -1em; display: block; text-decoration: none; visibility: hidden; text-align: center; font-weight: normal; } #content h1 > a.anchor:before, h2 > a.anchor:before, h3 > a.anchor:before, #toctitle > a.anchor:before, .sidebarblock > .content > .title > a.anchor:before, h4 > a.anchor:before, h5 > a.anchor:before, h6 > a.anchor:before { content: '\00A7'; font-size: .85em; vertical-align: text-top; display: block; margin-top: 0.05em; } #content h1:hover > a.anchor, #content h1 > a.anchor:hover, h2:hover > a.anchor, h2 > a.anchor:hover, h3:hover > a.anchor, #toctitle:hover > a.anchor, .sidebarblock > .content > .title:hover > a.anchor, h3 > a.anchor:hover, #toctitle > a.anchor:hover, .sidebarblock > .content > .title > a.anchor:hover, h4:hover > a.anchor, h4 > a.anchor:hover, h5:hover > a.anchor, h5 > a.anchor:hover, h6:hover > a.anchor, h6 > a.anchor:hover { visibility: visible; } #content h1 > a.link, h2 > a.link, h3 > a.link, #toctitle > a.link, .sidebarblock > .content > .title > a.link, h4 > a.link, h5 > a.link, h6 > a.link { color: #465158; text-decoration: none; } #content h1 > a.link:hover, h2 > a.link:hover, h3 > a.link:hover, #toctitle > a.link:hover, .sidebarblock > .content > .title > a.link:hover, h4 > a.link:hover, h5 > a.link:hover, h6 > a.link:hover { color: #3b444a; } .imageblock, .literalblock, .listingblock, .mathblock, .verseblock, .videoblock { margin-bottom: 1.25em; } .admonitionblock td.content > .title, .exampleblock > .title, .imageblock > .title, .listingblock > .title, .literalblock > .title, .mathblock > .title, .openblock > .title, .paragraph > .title, .quoteblock > .title, .sidebarblock > .title, .tableblock > .title, .verseblock > .title, .videoblock > .title, .dlist > .title, .olist > .title, .ulist > .title, .qlist > .title, .hdlist > .title { text-align: left; font-weight: bold; } .tableblock > caption { text-align: left; font-weight: bold; white-space: nowrap; overflow: visible; max-width: 0; } table.tableblock #preamble > .sectionbody > .paragraph:first-of-type p { font-size: inherit; } .admonitionblock > table { border: 0; background: none; width: 100%; } .admonitionblock > table td.icon { text-align: center; width: 80px; } .admonitionblock > table td.icon img { max-width: none; } .admonitionblock > table td.icon .title { font-weight: bold; text-transform: uppercase; } .admonitionblock > table td.content { padding-left: 1.125em; padding-right: 1.25em; border-left: 1px solid #dddddd; color: #909ea7; } .admonitionblock > table td.content > :last-child > :last-child { margin-bottom: 0; } .exampleblock > .content { border-style: solid; border-width: 1px; border-color: #e6e6e6; margin-bottom: 1.25em; padding: 1.25em; background: white; -webkit-border-radius: 6px; border-radius: 6px; } .exampleblock > .content > :first-child { margin-top: 0; } .exampleblock > .content > :last-child { margin-bottom: 0; } .exampleblock > .content h1, .exampleblock > .content h2, .exampleblock > .content h3, .exampleblock > .content #toctitle, .sidebarblock.exampleblock > .content > .title, .exampleblock > .content h4, .exampleblock > .content h5, .exampleblock > .content h6, .exampleblock > .content p { color: #333333; } .exampleblock > .content h1, .exampleblock > .content h2, .exampleblock > .content h3, .exampleblock > .content #toctitle, .sidebarblock.exampleblock > .content > .title, .exampleblock > .content h4, .exampleblock > .content h5, .exampleblock > .content h6 { line-height: 1; margin-bottom: 0.625em; } .exampleblock > .content h1.subheader, .exampleblock > .content h2.subheader, .exampleblock > .content h3.subheader, .exampleblock > .content .subheader#toctitle, .sidebarblock.exampleblock > .content > .subheader.title, .exampleblock > .content h4.subheader, .exampleblock > .content h5.subheader, .exampleblock > .content h6.subheader { line-height: 1.4; } .exampleblock.result > .content { -webkit-box-shadow: 0 1px 8px #d9d9d9; box-shadow: 0 1px 8px #d9d9d9; } .sidebarblock { border-style: solid; border-width: 1px; border-color: #d9d9d9; margin-bottom: 1.25em; padding: 1.25em; background: #f2f2f2; -webkit-border-radius: 6px; border-radius: 6px; } .sidebarblock > :first-child { margin-top: 0; } .sidebarblock > :last-child { margin-bottom: 0; } .sidebarblock h1, .sidebarblock h2, .sidebarblock h3, .sidebarblock #toctitle, .sidebarblock > .content > .title, .sidebarblock h4, .sidebarblock h5, .sidebarblock h6, .sidebarblock p { color: #333333; } .sidebarblock h1, .sidebarblock h2, .sidebarblock h3, .sidebarblock #toctitle, .sidebarblock > .content > .title, .sidebarblock h4, .sidebarblock h5, .sidebarblock h6 { line-height: 1; margin-bottom: 0.625em; } .sidebarblock h1.subheader, .sidebarblock h2.subheader, .sidebarblock h3.subheader, .sidebarblock .subheader#toctitle, .sidebarblock > .content > .subheader.title, .sidebarblock h4.subheader, .sidebarblock h5.subheader, .sidebarblock h6.subheader { line-height: 1.4; } .sidebarblock > .content > .title { color: #6c818f; margin-top: 0; line-height: 1.5; } .exampleblock > .content > :last-child > :last-child, .exampleblock > .content .olist > ol > li:last-child > :last-child, .exampleblock > .content .ulist > ul > li:last-child > :last-child, .exampleblock > .content .qlist > ol > li:last-child > :last-child, .sidebarblock > .content > :last-child > :last-child, .sidebarblock > .content .olist > ol > li:last-child > :last-child, .sidebarblock > .content .ulist > ul > li:last-child > :last-child, .sidebarblock > .content .qlist > ol > li:last-child > :last-child { margin-bottom: 0; } .literalblock pre:not([class]), .listingblock pre:not([class]) { background: #eeeeee; } .literalblock pre, .literalblock pre[class], .listingblock pre, .listingblock pre[class] { border-width: 1px; border-style: solid; border-color: #cccccc; -webkit-border-radius: 6px; border-radius: 6px; padding: 0.5em; word-wrap: break-word; } .literalblock pre.nowrap, .literalblock pre[class].nowrap, .listingblock pre.nowrap, .listingblock pre[class].nowrap { overflow-x: auto; white-space: pre; word-wrap: normal; } .literalblock pre > code, .literalblock pre[class] > code, .listingblock pre > code, .listingblock pre[class] > code { display: block; } @media only screen { .literalblock pre, .literalblock pre[class], .listingblock pre, .listingblock pre[class] { font-size: 0.76em; } } @media only screen and (min-width: 768px) { .literalblock pre, .literalblock pre[class], .listingblock pre, .listingblock pre[class] { font-size: 0.855em; } } @media only screen and (min-width: 1280px) { .literalblock pre, .literalblock pre[class], .listingblock pre, .listingblock pre[class] { font-size: 0.95em; } } .listingblock pre.highlight { padding: 0; } .listingblock pre.highlight > code { padding: 0.5em; } .listingblock > .content { position: relative; } .listingblock:hover code[class*=" language-"]:before { text-transform: uppercase; font-size: 0.9em; color: #999; position: absolute; top: 0.375em; right: 0.375em; } .listingblock:hover code.asciidoc:before { content: "asciidoc"; } .listingblock:hover code.clojure:before { content: "clojure"; } .listingblock:hover code.css:before { content: "css"; } .listingblock:hover code.groovy:before { content: "groovy"; } .listingblock:hover code.html:before { content: "html"; } .listingblock:hover code.java:before { content: "java"; } .listingblock:hover code.javascript:before { content: "javascript"; } .listingblock:hover code.python:before { content: "python"; } .listingblock:hover code.ruby:before { content: "ruby"; } .listingblock:hover code.sass:before { content: "sass"; } .listingblock:hover code.scss:before { content: "scss"; } .listingblock:hover code.xml:before { content: "xml"; } .listingblock:hover code.yaml:before { content: "yaml"; } .listingblock.terminal pre .command:before { content: attr(data-prompt); padding-right: 0.5em; color: #999; } .listingblock.terminal pre .command:not([data-prompt]):before { content: '$'; } table.pyhltable { border: 0; margin-bottom: 0; } table.pyhltable td { vertical-align: top; padding-top: 0; padding-bottom: 0; } table.pyhltable td.code { padding-left: .75em; padding-right: 0; } .highlight.pygments .lineno, table.pyhltable td:not(.code) { color: #999; padding-left: 0; padding-right: .5em; border-right: 1px solid #dddddd; } .highlight.pygments .lineno { display: inline-block; margin-right: .25em; } table.pyhltable .linenodiv { background-color: transparent !important; padding-right: 0 !important; } .quoteblock { margin: 0 0 1.25em; padding: 0.5625em 1.25em 0 1.1875em; border-left: 1px solid #dddddd; } .quoteblock blockquote { margin: 0 0 1.25em 0; padding: 0 0 0.5625em 0; border: 0; } .quoteblock blockquote > .paragraph:last-child p { margin-bottom: 0; } .quoteblock .attribution { margin-top: -.25em; padding-bottom: 0.5625em; font-size: 0.8125em; color: #748590; } .quoteblock .attribution br { display: none; } .quoteblock .attribution cite { display: block; margin-bottom: 0.625em; } table thead th, table tfoot th { font-weight: bold; } table.tableblock.grid-all { border-collapse: separate; border-spacing: 1px; -webkit-border-radius: 6px; border-radius: 6px; border-top: 0 solid #dddddd; border-bottom: 0 solid #dddddd; } table.tableblock.frame-topbot, table.tableblock.frame-none { border-left: 0; border-right: 0; } table.tableblock.frame-sides, table.tableblock.frame-none { border-top: 0; border-bottom: 0; } table.tableblock td .paragraph:last-child p > p:last-child, table.tableblock th > p:last-child, table.tableblock td > p:last-child { margin-bottom: 0; } th.tableblock.halign-left, td.tableblock.halign-left { text-align: left; } th.tableblock.halign-right, td.tableblock.halign-right { text-align: right; } th.tableblock.halign-center, td.tableblock.halign-center { text-align: center; } th.tableblock.valign-top, td.tableblock.valign-top { vertical-align: top; } th.tableblock.valign-bottom, td.tableblock.valign-bottom { vertical-align: bottom; } th.tableblock.valign-middle, td.tableblock.valign-middle { vertical-align: middle; } tbody tr th { display: table-cell; line-height: 1.5; background: none; } tbody tr th, tbody tr th p, tfoot tr th, tfoot tr th p { color: #222222; font-weight: bold; } td > div.verse { white-space: pre; } ol { margin-left: 0.25em; } ul li ol { margin-left: 0; } dl dd { margin-left: 1.125em; } dl dd:last-child, dl dd:last-child > :last-child { margin-bottom: 0; } ol > li p, ul > li p, ul dd, ol dd, .olist .olist, .ulist .ulist, .ulist .olist, .olist .ulist { margin-bottom: 0.625em; } ul.unstyled, ol.unnumbered, ul.checklist, ul.none { list-style-type: none; } ul.unstyled, ol.unnumbered, ul.checklist { margin-left: 0.625em; } ul.checklist li > p:first-child > i[class^="icon-check"]:first-child, ul.checklist li > p:first-child > input[type="checkbox"]:first-child { margin-right: 0.25em; } ul.checklist li > p:first-child > input[type="checkbox"]:first-child { position: relative; top: 1px; } ul.inline { margin: 0 auto 0.625em auto; margin-left: -1.375em; margin-right: 0; padding: 0; list-style: none; overflow: hidden; } ul.inline > li { list-style: none; float: left; margin-left: 1.375em; display: block; } ul.inline > li > * { display: block; } .unstyled dl dt { font-weight: normal; font-style: normal; } ol.arabic { list-style-type: decimal; } ol.decimal { list-style-type: decimal-leading-zero; } ol.loweralpha { list-style-type: lower-alpha; } ol.upperalpha { list-style-type: upper-alpha; } ol.lowerroman { list-style-type: lower-roman; } ol.upperroman { list-style-type: upper-roman; } ol.lowergreek { list-style-type: lower-greek; } .hdlist > table, .colist > table { border: 0; background: none; } .hdlist > table > tbody > tr, .colist > table > tbody > tr { background: none; } td.hdlist1 { padding-right: .75em; font-weight: bold; } td.hdlist1, td.hdlist2 { vertical-align: top; } .literalblock + .colist, .listingblock + .colist { margin-top: -0.5em; } .colist > table tr > td:first-of-type { padding: 0 .75em; line-height: 1; } .colist > table tr > td:last-of-type { padding: 0.25em 0; } .qanda > ol > li > p > em:only-child { color: #373737; } .thumb, .th { line-height: 0; display: inline-block; border: solid 4px white; -webkit-box-shadow: 0 0 0 1px #dddddd; box-shadow: 0 0 0 1px #dddddd; } .imageblock.left, .imageblock[style*="float: left"] { margin: 0.25em 0.625em 1.25em 0; } .imageblock.right, .imageblock[style*="float: right"] { margin: 0.25em 0 1.25em 0.625em; } .imageblock > .title { margin-bottom: 0; } .imageblock.thumb, .imageblock.th { border-width: 6px; } .imageblock.thumb > .title, .imageblock.th > .title { padding: 0 0.125em; } .image.left, .image.right { margin-top: 0.25em; margin-bottom: 0.25em; display: inline-block; line-height: 0; } .image.left { margin-right: 0.625em; } .image.right { margin-left: 0.625em; } a.image { text-decoration: none; } span.footnote, span.footnoteref { vertical-align: super; font-size: 0.875em; } span.footnote a, span.footnoteref a { text-decoration: none; } #footnotes { padding-top: 0.75em; padding-bottom: 0.75em; margin-bottom: 0.625em; } #footnotes hr { width: 20%; min-width: 6.25em; margin: -.25em 0 .75em 0; border-width: 1px 0 0 0; } #footnotes .footnote { padding: 0 0.375em; line-height: 1.3; font-size: 0.875em; margin-left: 1.2em; text-indent: -1.2em; margin-bottom: .2em; } #footnotes .footnote a:first-of-type { font-weight: bold; text-decoration: none; } #footnotes .footnote:last-of-type { margin-bottom: 0; } #content #footnotes { margin-top: -0.625em; margin-bottom: 0; padding: 0.75em 0; } .gist .file-data > table { border: none; background: #fff; width: 100%; margin-bottom: 0; } .gist .file-data > table td.line-data { width: 99%; } div.unbreakable { page-break-inside: avoid; } .big { font-size: larger; } .small { font-size: smaller; } .underline { text-decoration: underline; } .overline { text-decoration: overline; } .line-through { text-decoration: line-through; } .aqua { color: #00bfbf; } .aqua-background { background-color: #00fafa; } .black { color: black; } .black-background { background-color: black; } .blue { color: #0000bf; } .blue-background { background-color: #0000fa; } .fuchsia { color: #bf00bf; } .fuchsia-background { background-color: #fa00fa; } .gray { color: #606060; } .gray-background { background-color: #7d7d7d; } .green { color: #006000; } .green-background { background-color: #007d00; } .lime { color: #00bf00; } .lime-background { background-color: #00fa00; } .maroon { color: #600000; } .maroon-background { background-color: #7d0000; } .navy { color: #000060; } .navy-background { background-color: #00007d; } .olive { color: #606000; } .olive-background { background-color: #7d7d00; } .purple { color: #600060; } .purple-background { background-color: #7d007d; } .red { color: #bf0000; } .red-background { background-color: #fa0000; } .silver { color: #909090; } .silver-background { background-color: #bcbcbc; } .teal { color: #006060; } .teal-background { background-color: #007d7d; } .white { color: #bfbfbf; } .white-background { background-color: #fafafa; } .yellow { color: #bfbf00; } .yellow-background { background-color: #fafa00; } span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; } .admonitionblock td.icon [class^="icon-"]:before { font-size: 2.5em; text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5); cursor: default; } .admonitionblock td.icon .icon-note:before { content: "\f05a"; color: #444444; color: #333333; } .admonitionblock td.icon .icon-tip:before { content: "\f0eb"; text-shadow: 1px 1px 2px rgba(155, 155, 0, 0.8); color: #111; } .admonitionblock td.icon .icon-warning:before { content: "\f071"; color: #bf6900; } .admonitionblock td.icon .icon-caution:before { content: "\f06d"; color: #bf3400; } .admonitionblock td.icon .icon-important:before { content: "\f06a"; color: #bf0000; } .conum { display: inline-block; color: white !important; background-color: black; -webkit-border-radius: 100px; border-radius: 100px; text-align: center; width: 20px; height: 20px; font-size: 12px; font-weight: bold; line-height: 20px; font-family: Arial, sans-serif; font-style: normal; position: relative; top: -2px; letter-spacing: -1px; } .conum * { color: white !important; } .conum + b { display: none; } .conum:after { content: attr(data-value); } .conum:not([data-value]):empty { display: none; } .listingblock code { white-space: pre; overflow: auto; overflow-wrap: normal; /* needed for webkit browsers */ } #toc ul.sectlevel0 > li > a { font-style: normal; font-weight: bold; } h4 { color: #6c818f; } .literalblock > .content > pre, .listingblock > .content > pre { -webkit-border-radius: 6px; border-radius: 6px; margin-left: 2em; margin-right: 2em; } .admonitionblock { margin-left: 2em; margin-right: 2em; } .admonitionblock > table { border: 1px solid #609060; border-top-width: 1.5em; background-color: #e9ffe9; border-collapse: separate; -webkit-border-radius: 0; border-radius: 0; } .admonitionblock > table td.icon { padding-top: .5em; padding-bottom: .5em; } .admonitionblock > table td.content { padding: .5em 1em; color: black; font-size: .9em; border-left: none; } .sidebarblock { background-color: #e8ecef; border-color: #ccc; } .sidebarblock > .content > .title { color: #444444; } table.tableblock.grid-all { border-collapse: collapse; -webkit-border-radius: 0; border-radius: 0; } table.tableblock.grid-all th.tableblock, table.tableblock.grid-all td.tableblock { border-bottom: 1px solid #aaa; } #footer { background-color: #465158; padding: 2em; } #footer-text { color: #eee; font-size: 0.8em; text-align: center; } .tabs{position:relative;margin:40px auto;width:1024px;max-width:100%;overflow:hidden;padding-top:10px;margin-bottom:60px}.tabs input{position:absolute;z-index:1000;height:50px;left:0;top:0;opacity:0;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";filter:alpha(opacity=0);cursor:pointer;margin:0}.tabs input:hover+label{background:#e08f24}.tabs label{background:#e9ffe9;color:#1a1a1a;font-size:15px;line-height:50px;height:60px;position:relative;top:0;padding:0 20px;float:left;display:block;letter-spacing:1px;text-transform:uppercase;font-weight:bold;text-align:center;box-shadow:2px 0 2px rgba(0,0,0,0.1),-2px 0 2px rgba(0,0,0,0.1);box-sizing:border-box;-webkit-transition:all 150ms ease 0s;transition:all 150ms ease 0s}.tabs label:hover{cursor:pointer}.tabs label:after{content:'';background:#609060;position:absolute;bottom:-2px;left:0;width:100%;height:2px;display:block}.tabs-2 input{width:50%}.tabs-2 input.tab-selector-1{left:0%}.tabs-2 input.tab-selector-2{left:50%}.tabs-2 label{width:50%}.tabs-3 input{width:33.3333333333%}.tabs-3 input.tab-selector-1{left:0%}.tabs-3 input.tab-selector-2{left:33.3333333333%}.tabs-3 input.tab-selector-3{left:66.6666666667%}.tabs-3 label{width:33.3333333333%}.tabs-4 input{width:25%}.tabs-4 input.tab-selector-1{left:0%}.tabs-4 input.tab-selector-2{left:25%}.tabs-4 input.tab-selector-3{left:50%}.tabs-4 input.tab-selector-4{left:75%}.tabs-4 label{width:25%}.tabs-5 input{width:20%}.tabs-5 input.tab-selector-1{left:0%}.tabs-5 input.tab-selector-2{left:20%}.tabs-5 input.tab-selector-3{left:40%}.tabs-5 input.tab-selector-4{left:60%}.tabs-5 input.tab-selector-5{left:80%}.tabs-5 label{width:20%}.tabs-6 input{width:16.6666666667%}.tabs-6 input.tab-selector-1{left:0%}.tabs-6 input.tab-selector-2{left:16.6666666667%}.tabs-6 input.tab-selector-3{left:33.3333333333%}.tabs-6 input.tab-selector-4{left:50%}.tabs-6 input.tab-selector-5{left:66.6666666667%}.tabs-6 input.tab-selector-6{left:83.3333333333%}.tabs-6 label{width:16.6666666667%}.tabs-7 input{width:14.2857142857%}.tabs-7 input.tab-selector-1{left:0%}.tabs-7 input.tab-selector-2{left:14.2857142857%}.tabs-7 input.tab-selector-3{left:28.5714285714%}.tabs-7 input.tab-selector-4{left:42.8571428571%}.tabs-7 input.tab-selector-5{left:57.1428571429%}.tabs-7 input.tab-selector-6{left:71.4285714286%}.tabs-7 input.tab-selector-7{left:85.7142857143%}.tabs-7 label{width:14.2857142857%}.tabs label:first-of-type{z-index:4}.tab-label-2{z-index:4}.tab-label-3{z-index:3}.tab-label-4{z-index:2}.tabs input:checked+label{background:#609060;color:#fefefe;z-index:6}.clear-shadow{clear:both}.tabcontent{height:auto;width:100%;float:left;position:relative;z-index:5;background:#eee;top:-10px;box-sizing:border-box}.tabcontent>div{position:relative;float:left;width:0;height:0;box-sizing:border-box;top:0;left:0;z-index:1;opacity:0;background:#eee}.tabcontent .CodeRay{background-color:#fefefe}.tabs .tab-selector-1:checked ~ .tabcontent .tabcontent-1{z-index:100;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";filter:alpha(opacity=100);opacity:1;width:100%;height:auto;width:100%;height:auto;padding-top:30px}.tabs .tab-selector-2:checked ~ .tabcontent .tabcontent-2{z-index:100;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";filter:alpha(opacity=100);opacity:1;width:100%;height:auto;width:100%;height:auto;padding-top:30px}.tabs .tab-selector-3:checked ~ .tabcontent .tabcontent-3{z-index:100;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";filter:alpha(opacity=100);opacity:1;width:100%;height:auto;width:100%;height:auto;padding-top:30px}.tabs .tab-selector-4:checked ~ .tabcontent .tabcontent-4{z-index:100;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";filter:alpha(opacity=100);opacity:1;width:100%;height:auto;width:100%;height:auto;padding-top:30px}.tabs .tab-selector-5:checked ~ .tabcontent .tabcontent-5{z-index:100;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";filter:alpha(opacity=100);opacity:1;width:100%;height:auto;width:100%;height:auto;padding-top:30px}.tabs .tab-selector-6:checked ~ .tabcontent .tabcontent-6{z-index:100;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";filter:alpha(opacity=100);opacity:1;width:100%;height:auto;width:100%;height:auto;padding-top:30px}.tabs .tab-selector-7:checked ~ .tabcontent .tabcontent-7{z-index:100;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";filter:alpha(opacity=100);opacity:1;width:100%;height:auto;width:100%;height:auto;padding-top:30px} .invisible {color: rgba(0,0,0,0); font-size: 0;} </style> <style> /*! Stylesheet for CodeRay to loosely match GitHub themes | MIT License */ pre.CodeRay{background:#f7f7f8} .CodeRay .line-numbers{border-right:1px solid;opacity:.35;padding:0 .5em 0 0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none} .CodeRay span.line-numbers{display:inline-block;margin-right:.75em} .CodeRay .line-numbers strong{color:#000} table.CodeRay{border-collapse:separate;border:0;margin-bottom:0;background:none} table.CodeRay td{vertical-align:top;line-height:inherit} table.CodeRay td.line-numbers{text-align:right} table.CodeRay td.code{padding:0 0 0 .75em} .CodeRay .debug{color:#fff!important;background:navy!important} .CodeRay .annotation{color:#007} .CodeRay .attribute-name{color:navy} .CodeRay .attribute-value{color:#700} .CodeRay .binary{color:#509} .CodeRay .comment{color:#998;font-style:italic} .CodeRay .char{color:#04d} .CodeRay .char .content{color:#04d} .CodeRay .char .delimiter{color:#039} .CodeRay .class{color:#458;font-weight:bold} .CodeRay .complex{color:#a08} .CodeRay .constant,.CodeRay .predefined-constant{color:teal} .CodeRay .color{color:#099} .CodeRay .class-variable{color:#369} .CodeRay .decorator{color:#b0b} .CodeRay .definition{color:#099} .CodeRay .delimiter{color:#000} .CodeRay .doc{color:#970} .CodeRay .doctype{color:#34b} .CodeRay .doc-string{color:#d42} .CodeRay .escape{color:#666} .CodeRay .entity{color:#800} .CodeRay .error{color:#808} .CodeRay .exception{color:inherit} .CodeRay .filename{color:#099} .CodeRay .function{color:#900;font-weight:bold} .CodeRay .global-variable{color:teal} .CodeRay .hex{color:#058} .CodeRay .integer,.CodeRay .float{color:#099} .CodeRay .include{color:#555} .CodeRay .inline{color:#000} .CodeRay .inline .inline{background:#ccc} .CodeRay .inline .inline .inline{background:#bbb} .CodeRay .inline .inline-delimiter{color:#d14} .CodeRay .inline-delimiter{color:#d14} .CodeRay .important{color:#555;font-weight:bold} .CodeRay .interpreted{color:#b2b} .CodeRay .instance-variable{color:teal} .CodeRay .label{color:#970} .CodeRay .local-variable{color:#963} .CodeRay .octal{color:#40e} .CodeRay .predefined{color:#369} .CodeRay .preprocessor{color:#579} .CodeRay .pseudo-class{color:#555} .CodeRay .directive{font-weight:bold} .CodeRay .type{font-weight:bold} .CodeRay .predefined-type{color:inherit} .CodeRay .reserved,.CodeRay .keyword{color:#000;font-weight:bold} .CodeRay .key{color:#808} .CodeRay .key .delimiter{color:#606} .CodeRay .key .char{color:#80f} .CodeRay .value{color:#088} .CodeRay .regexp .delimiter{color:#808} .CodeRay .regexp .content{color:#808} .CodeRay .regexp .modifier{color:#808} .CodeRay .regexp .char{color:#d14} .CodeRay .regexp .function{color:#404;font-weight:bold} .CodeRay .string{color:#d20} .CodeRay .string .string .string{background:#ffd0d0} .CodeRay .string .content{color:#d14} .CodeRay .string .char{color:#d14} .CodeRay .string .delimiter{color:#d14} .CodeRay .shell{color:#d14} .CodeRay .shell .delimiter{color:#d14} .CodeRay .symbol{color:#990073} .CodeRay .symbol .content{color:#a60} .CodeRay .symbol .delimiter{color:#630} .CodeRay .tag{color:teal} .CodeRay .tag-special{color:#d70} .CodeRay .variable{color:#036} .CodeRay .insert{background:#afa} .CodeRay .delete{background:#faa} .CodeRay .change{color:#aaf;background:#007} .CodeRay .head{color:#f8f;background:#505} .CodeRay .insert .insert{color:#080} .CodeRay .delete .delete{color:#800} .CodeRay .change .change{color:#66f} .CodeRay .head .head{color:#f4f} </style> <!-- --> <!-- Matomo --> <script> var _paq = window._paq = window._paq || []; /* We explicitly disable cookie tracking to avoid privacy issues */ _paq.push(['disableCookies']); /* tracker methods like "setCustomDimension" should be called before "trackPageView" */ _paq.push(['trackPageView']); _paq.push(['enableLinkTracking']); (function() { var u="https://analytics.apache.org/"; _paq.push(['setTrackerUrl', u+'matomo.php']); _paq.push(['setSiteId', '27']); var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s); })(); </script> <!-- End Matomo Code --> </head> <body class="book toc2 toc-left"> <div id="header"> <div id="toc" class="toc2"> <div id="toctitle">Table of Contents</div> <ul class="sectlevel0"> <li><a href="#_developer_documentation">Developer Documentation</a></li> <li><a href="#_contributing">Contributing</a> <ul class="sectlevel1"> <li><a href="#ways-to-contribute">Ways to Contribute</a> <ul class="sectlevel2"> <li><a href="#_mailing_list_participation">Mailing List Participation</a></li> <li><a href="#_testing_releases">Testing Releases</a></li> <li><a href="#_reviewing_changes">Reviewing Changes</a></li> <li><a href="#_writing_documentation">Writing Documentation</a></li> <li><a href="#_reporting_bugs">Reporting Bugs</a></li> <li><a href="#_tinkerpop_libraries">TinkerPop Libraries</a></li> </ul> </li> <li><a href="#_considering_code_changes">Considering Code Changes</a></li> <li><a href="#_contributing_code_changes">Contributing Code Changes</a></li> <li><a href="#versioning">Versioning</a></li> </ul> </li> <li><a href="#development-environment">Development Environment</a> <ul class="sectlevel1"> <li><a href="#system-configuration">System Configuration</a> <ul class="sectlevel2"> <li><a href="#groovy-environment">Groovy Environment</a></li> <li><a href="#documentation-environment">Documentation Environment</a></li> <li><a href="#python-environment">Python Environment</a></li> <li><a href="#dotnet-environment">DotNet Environment</a></li> <li><a href="#nodejs-environment">JavaScript Environment</a></li> <li><a href="#go-environment">Go Environment</a></li> <li><a href="#docker-environment">Docker Environment</a></li> <li><a href="#release-environment">Release Environment</a></li> </ul> </li> <li><a href="#building-testing">Building and Testing</a></li> <li><a href="#building-on-windows">Building On Windows</a></li> <li><a href="#docker-integration">Docker Integration</a></li> <li><a href="#docker-testing">Testing Sub-Modules with Docker</a></li> <li><a href="#intellij">Intellij Usage</a> <ul class="sectlevel2"> <li><a href="#_setup">Setup</a></li> <li><a href="#_debugging">Debugging</a> <ul class="sectlevel3"> <li><a href="#debug-java">Java</a></li> <li><a href="#debug-python">Python</a></li> </ul> </li> </ul> </li> </ul> </li> <li><a href="#_for_committers">For Committers</a> <ul class="sectlevel1"> <li><a href="#initial-setup">Initial Setup</a></li> <li><a href="#_communication">Communication</a></li> <li><a href="#_media_content_submission">Media Content Submission</a></li> <li><a href="#_release_notes">Release Notes</a></li> <li><a href="#branches">Branches</a></li> <li><a href="#_tags">Tags</a></li> <li><a href="#runtimes">Runtimes</a></li> <li><a href="#_issue_tracker_conventions">Issue Tracker Conventions</a></li> <li><a href="#_code_style">Code Style</a></li> <li><a href="#_build_server">Build Server</a></li> <li><a href="#_deprecation">Deprecation</a></li> <li><a href="#developing-tests">Developing Tests</a> <ul class="sectlevel2"> <li><a href="#gremlin-language-test-cases">Gremlin Language Test Cases</a> <ul class="sectlevel3"> <li><a href="#_scenario_name">Scenario Name</a></li> <li><a href="#_given">Given</a></li> <li><a href="#_when">When</a></li> <li><a href="#_then">Then</a></li> <li><a href="#gherkin-tags">Tags</a></li> </ul> </li> <li><a href="#gremlin-socket-server-tests">Gremlin Socket Server Tests</a></li> </ul> </li> <li><a href="#_developing_benchmarks">Developing Benchmarks</a></li> <li><a href="#rtc">Review then Commit</a> <ul class="sectlevel2"> <li><a href="#pull-requests">Pull Requests</a></li> </ul> </li> <li><a href="#dependencies">Dependencies</a> <ul class="sectlevel2"> <li><a href="#_source_license_and_notice">Source LICENSE and NOTICE</a></li> <li><a href="#_binary_license_and_notice">Binary LICENSE and NOTICE</a></li> </ul> </li> <li><a href="#documentation">Documentation</a> <ul class="sectlevel2"> <li><a href="#_asciidoc_formatting_tips">Asciidoc Formatting Tips</a></li> </ul> </li> <li><a href="#site">Site</a></li> <li><a href="#logging">Logging</a></li> </ul> </li> <li><a href="#_for_providers">For Providers</a></li> <li><a href="#_release_process">Release Process</a> <ul class="sectlevel1"> <li><a href="#_development_versions">Development Versions</a></li> <li><a href="#_release_manager_requirements">Release Manager Requirements</a></li> <li><a href="#_pre_flight_check">Pre-flight Check</a></li> <li><a href="#_release_candidate">Release Candidate</a></li> <li><a href="#_pmc_vote">PMC Vote</a></li> <li><a href="#_release_promote">Release &amp; Promote</a></li> <li><a href="#_post_release_tasks">Post-release Tasks</a></li> <li><a href="#email-templates">Email Templates</a> <ul class="sectlevel2"> <li><a href="#_release_vote">Release VOTE</a></li> <li><a href="#_release_result_vote">Release RESULT VOTE</a></li> <li><a href="#_general_release_announcement">General Release Announcement</a></li> <li><a href="#_standard_tweet_text">Standard Tweet Text</a></li> </ul> </li> </ul> </li> <li><a href="#_administration">Administration</a> <ul class="sectlevel1"> <li><a href="#_new_committers">New Committers</a></li> <li><a href="#_new_pmc_members">New PMC Members</a></li> <li><a href="#_board_reports">Board Reports</a></li> <li><a href="#contributor-listing">Contributor Listing</a></li> </ul> </li> <li><a href="#meetings">Meetings</a> <ul class="sectlevel1"> <li><a href="#_november_23_2015">November 23, 2015</a></li> <li><a href="#_october_29_2015">October 29, 2015</a></li> </ul> </li> </ul> </div> </div> <div id="content"> <div id="preamble"> <div class="sectionbody"> <div class="imageblock"> <div class="content"> <a class="image" href="https://tinkerpop.apache.org"><img src="../../images/apache-tinkerpop-logo.png" alt="apache tinkerpop logo" width="500"></a> </div> </div> <div class="paragraph"> <p><strong>3.7.3</strong></p> </div> </div> </div> <h1 id="_developer_documentation" class="sect0">Developer Documentation</h1> <div class="openblock partintro"> <div class="content"> <div class="paragraph"> <p>This document contains information for TinkerPop developers, contributors, and community members. It focuses on technical information and other internal processes related to the project.</p> </div> <div class="paragraph"> <p><span class="image"><img src="../../images/jan-de-bray.jpg" alt="jan de bray"></span></p> </div> </div> </div> <h1 id="_contributing" class="sect0">Contributing</h1> <div class="openblock partintro"> <div class="content"> Contributions via GitHub pull requests are gladly accepted from their original author. By submitting any copyrighted material via pull request, email, or other means you agree to license the material under the project&#8217;s open source license and warrant that you have the legal authority to do so. </div> </div> <div class="sect1"> <h2 id="ways-to-contribute">Ways to Contribute</h2> <div class="sectionbody"> <div class="paragraph"> <p><span class="image left"><img src="../../images/gremlin-apache.png" alt="gremlin apache" width="250"></span> While the concept of an open source contribution can refer to doing development work on the code base, there are many other ways outside of coding to contribute to Apache TinkerPop. Participating on the various mailing lists, offering ideas, reporting bugs, writing documentation are all welcome contributions to the project that help improve the TinkerPop. This section of the document is designed to help provide some structure for potential contributors and to give them ideas for how they could get started becoming more involved in the TinkerPop community.</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> As a quick acknowledgment, this section was influenced by Apache Spark&#8217;s well organized <a href="https://cwiki.apache.org/confluence/display/SPARK/Contributing+to+Spark">"Contributing to Spark"</a> document. </td> </tr> </table> </div> <div class="sect2"> <h3 id="_mailing_list_participation">Mailing List Participation</h3> <div class="paragraph"> <p>TinkerPop has two mailing lists: <a href="https://groups.google.com/forum/#!forum/gremlin-users">gremlin-users</a> and <a href="https://lists.apache.org/list.html?dev@tinkerpop.apache.org">dev</a>. Subscribing to and participating on one or both of these mailing lists is a great way to contribute to TinkerPop. Helping users with their problems by answering their questions on gremlin-users is a massive help to the community and an easy way for the community to become familiar with the contributor.</p> </div> <div class="paragraph"> <p>The <a href="mailto:dev@tinkerpop.apache.org">dev@tinkerpop.apache.org</a> mailing list is where all design discussion, early feature announcements, release discussions and other similar communication takes place. Having insight into the project at this level, will yield a lot of information about the day-to-day course of TinkerPop and provides a way to help shape the direction of the project.</p> </div> </div> <div class="sect2"> <h3 id="_testing_releases">Testing Releases</h3> <div class="paragraph"> <p>Proposed TinkerPop releases are not official until after they are evaluated by the community. The process of evaluation occurs on the <a href="mailto:dev@tinkerpop.apache.org">dev@tinkerpop.apache.org</a> mailing list where the community is invited to review release artifacts and vote if they should be released or not. Anyone is free to comment and provide a vote (+1/+0/-1) on a release. Note that votes of non-PMC members are considered non-binding, but are certainly considered in the review process.</p> </div> </div> <div class="sect2"> <h3 id="_reviewing_changes">Reviewing Changes</h3> <div class="paragraph"> <p>Virtually all changes to TinkerPop&#8217;s source code are performed via GitHub <a href="https://github.com/apache/tinkerpop/pulls">pull requests</a>. When a pull request is issued it undergoes a <a href="#rtc">review process</a> prior to being merged to a release branch. Anyone is free to comment and provide a vote (+1/+0/-1) on a pull request. Note that votes of non-committers are considered non-binding, but are certainly considered in the review process.</p> </div> </div> <div class="sect2"> <h3 id="_writing_documentation">Writing Documentation</h3> <div class="paragraph"> <p>Proposing a documentation change is quite similar to providing a source code change, which is described below in more detail. Note that there is a difference between project documentation and the <a href="https://tinkerpop.apache.org/">TinkerPop web site</a>. The project documentation refers to the documentation that is published per release like, tutorials, reference documentation, and other similar pages. This content is not updated after release and remains static bound to that version. The TinkerPop web site refers to the static home page and its related content, such as <a href="https://tinkerpop.apache.org/gremlin.html">Understanding Gremlin</a>, <a href="https://tinkerpop.apache.org/providers.html">Data System Providers</a> and other similar pages. This content is not bound to any particular version and can be published at any time where new content simply replaces old content.</p> </div> <div class="paragraph"> <p>For both types of documentation, changes can be submitted via pull request. For project documentation, TinkerPop has a robust documentation system that is based on <a href="http://asciidoc.org/">asciidoc</a>. The content can be found in the <a href="https://github.com/apache/tinkerpop/tree/master/docs/src">docs/src</a>. Documentation is version specific, so consider the appropriate branch on which to submit the pull request so that the documentation is reflective of the version it is tied to. To view generated documentation locally, read more about environment configurations in the <a href="#documentation-environment">Documentation Environment</a> and <a href="#documentation">Contributor Documentation</a> sections.</p> </div> <div class="paragraph"> <p>For web site changes, the process is largely the same except that the documentation system is HTML based instead of Asciidoc. The content can be found in the source control tree at <a href="https://github.com/apache/tinkerpop/tree/master/docs/site">docs/site</a>. The web site is always published from the <code>master</code> branch as it is not bound to a version, so there is no need to submit a pull request to any other branches besides that one. If the change to this documentation involves changing provider listings in some way, first propose those changes on the <a href="mailto:dev@tinkerpop.apache.org">dev@tinkerpop.apache.org</a> mailing list and achieve consensus (wait time is 72 hours as usual). At that point, pull requests or direct changes can be submitted. Again, see the <a href="#documentation-environment">Documentation Environment</a> section for more information on how to generate the site locally.</p> </div> </div> <div class="sect2"> <h3 id="_reporting_bugs">Reporting Bugs</h3> <div class="paragraph"> <p>Well formed bug reports (especially when accompanied by a pull request that fixes the problem) are welcome contributions that ultimately help improve the quality of TinkerPop. Of course, a bug report is only as good as its reproduction steps and the surrounding details of the issue. When a bug is encountered, it should be documented in <a href="https://issues.apache.org/jira/browse/TINKERPOP">JIRA</a>, where it will likely trigger some discussion and ultimately a resolution.</p> </div> </div> <div class="sect2"> <h3 id="_tinkerpop_libraries">TinkerPop Libraries</h3> <div class="paragraph"> <p>TinkerPop is more than just the core source code repository. It is a rich ecosystem of libraries developed by <a href="https://tinkerpop.apache.org/providers.html">providers</a>, who look to use or extend upon the TinkerPop interfaces. TinkerPop is highly focused on its interfaces and a small set of reference implementations of those interfaces as part of its core code base. As such, large and independent new functionality is often rejected from inclusion in TinkerPop itself, however, it is very much encouraged that such a project stand on its own as separate repository. Projects like this that meet the TinkerPop <a href="https://tinkerpop.apache.org/docs/3.7.3/dev/provider/#policy-listing">listing policy</a> can be added to the TinkerPop web site and promoted there. Please see the current listing of such libraries on the TinkerPop <a href="https://tinkerpop.apache.org/#graph-systems">home page</a> to get an idea as to what kinds of libraries are available from the community. When in doubt about whether a library might be a good fit for hosting and ongoing maintenance in the TinkerPop project itself, please ask on the <a href="mailto:dev@tinkerpop.apache.org">dev@tinkerpop.apache.org</a> mailing list.</p> </div> </div> </div> </div> <div class="sect1"> <h2 id="_considering_code_changes">Considering Code Changes</h2> <div class="sectionbody"> <div class="paragraph"> <p>Code changes can take a fair bit of effort from both the contributor doing the work, as well as the people who will be reviewing and testing that work. It is beneficial to all involved that some basic considerations be made by the contributor to ensure the best use is made of everyone&#8217;s time.</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> For those who are trying to find a place to start to contribute, consider looking at unresolved issues that have the "trivial" priority as these issues are specifically set aside as <a href="https://issues.apache.org/jira/issues/?jql=project%20%3D%20TINKERPOP%20AND%20resolution%20%3D%20Unresolved%20AND%20priority%20%3D%20Trivial%20ORDER%20BY%20key%20DESC">low-hanging fruit</a> for newcomers. </td> </tr> </table> </div> <div class="paragraph"> <p>Before proceeding, contributors should evaluate if the proposed change is likely to be relevant, new and actionable:</p> </div> <div class="ulist"> <ul> <li> <p>Is it clear that code must change? Proposing a JIRA issue and pull request is appropriate only when a clear problem or change has been identified. When in doubt, email <a href="mailto:dev@tinkerpop.apache.org">dev@tinkerpop.apache.org</a> first about the possible change.</p> </li> <li> <p>Search the mailing list archives for related discussions. Often, the problem has been discussed before, with a resolution that doesn&#8217;t require a code change, or recording what kinds of changes will not be accepted as a resolution.</p> </li> <li> <p>Search <a href="https://issues.apache.org/jira/browse/TINKERPOP">JIRA</a> for existing issues.</p> </li> <li> <p>Is the scope of the change matched to the contributor&#8217;s level of experience? Anyone is qualified to suggest a typo fix, but refactoring a core feature in serialization or OLAP requires much more understanding of TinkerPop. Some changes require building up experience first.</p> </li> </ul> </div> <div class="paragraph"> <p>Before considering how to contribute code, it is useful to understand how code is reviewed, and why changes may be rejected. Simply put, changes that have many or large positives, and few negative effects or risks, are much more likely to be merged, and merged quickly. Risky and less valuable changes are very unlikely to be merged, and may be rejected outright rather than receive iterations of review.</p> </div> <div class="paragraph"> <p><strong>Positives</strong></p> </div> <div class="ulist"> <ul> <li> <p>Change has already been discussed and is known to committers</p> </li> <li> <p>Fixes the root cause of a bug in existing functionality</p> </li> <li> <p>Adds functionality or fixes a problem needed by a large number of users</p> </li> <li> <p>Simple, targeted</p> </li> <li> <p>Easily tested; has tests</p> </li> <li> <p>Reduces complexity and lines of code</p> </li> </ul> </div> <div class="paragraph"> <p><strong>Negatives, Risks</strong></p> </div> <div class="ulist"> <ul> <li> <p>Band-aids a symptom of a bug only</p> </li> <li> <p>Introduces complex new functionality, especially an API that needs to be supported</p> </li> <li> <p>Adds complexity that only helps a niche use case</p> </li> <li> <p>Adds user-space functionality that does not need to be maintained in TinkerPop, but could be hosted externally and promoted in <a href="https://tinkerpop.apache.org/#graph-systems">provider listings</a></p> </li> <li> <p>Changes a public API or semantics thus introducing a breaking change</p> </li> <li> <p>Adds large dependencies</p> </li> <li> <p>Changes versions of existing dependencies</p> </li> <li> <p>Adds a large amount of code</p> </li> <li> <p>Makes lots of modifications in one "big bang" change</p> </li> </ul> </div> </div> </div> <div class="sect1"> <h2 id="_contributing_code_changes">Contributing Code Changes</h2> <div class="sectionbody"> <div class="paragraph"> <p>Generally, TinkerPop uses <a href="https://issues.apache.org/jira/browse/TINKERPOP">JIRA</a> to track logical issues, including bugs and improvements, and uses GitHub pull requests to manage the review and merge of specific code changes. That is, JIRA issues are used to describe what should be fixed or changed, and high-level approaches, and pull requests describe how to implement that change in the project&#8217;s source code.</p> </div> <div class="paragraph"> <p>As a first step to making a contribution, consider <strong>JIRA</strong>:</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p>Find the existing JIRA ticket that the change pertains to.</p> <div class="olist loweralpha"> <ol class="loweralpha" type="a"> <li> <p>Do not create a new ticket if creating a change to address an existing issue in JIRA; add to the existing discussion and work instead.</p> </li> <li> <p>Look for existing pull requests that are linked from the ticket, to understand if someone is already working on the JIRA.</p> </li> </ol> </div> </li> <li> <p>If the change is new, then it usually needs a new ticket. However, trivial changes, where the change is virtually the same as the how it should change do not require a JIRA (e.g. "Fix typos in Foo javadoc").</p> </li> <li> <p>If required, create a new ticket:</p> <div class="olist loweralpha"> <ol class="loweralpha" type="a"> <li> <p>Provide a descriptive Title and a detailed Description. For bug reports, this should ideally include a short reproduction of the problem.</p> </li> <li> <p>Set required fields - these are detailed later in this document in the <a href="#_issue_tracker_conventions">Issue Tracker Conventions</a> section.</p> </li> </ol> </div> </li> <li> <p>If the change is a large change, consider inviting discussion on the issue at <a href="mailto:dev@tinkerpop.apache.org">dev@tinkerpop.apache.org</a> first before proceeding to implement the change.</p> </li> </ol> </div> <div class="paragraph"> <p>Next, <strong>make changes</strong> and prepare a <strong>pull request</strong>:</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p><a href="https://help.github.com/articles/fork-a-repo/">Fork and then clone</a> the Apache TinkerPop <a href="https://github.com/apache/tinkerpop">GitHub repository</a> if not already done.</p> </li> <li> <p>Make changes in the fork</p> <div class="olist loweralpha"> <ol class="loweralpha" type="a"> <li> <p>It is typically best to create a branch for the changes. Consider naming that branch after the JIRA issue number to easily track what that branch is for.</p> </li> <li> <p>Consider which release branch (e.g. <code>master</code>, <code>3.5-dev</code> - consult the <a href="https://tinkerpop.apache.org/docs/3.7.3/dev/developer/#branches">Branches Section</a> for more information) to create the development branch from in the first place. In other words, is the change to be targeted at a specific TinkerPop version (e.g. a patch to an older version)? When in doubt, please ask on <a href="mailto:dev@tinkerpop.apache.org">dev@tinkerpop.apache.org</a>.</p> </li> </ol> </div> </li> <li> <p>Build the project and run tests.</p> <div class="olist loweralpha"> <ol class="loweralpha" type="a"> <li> <p>A simple build can be accomplished with maven: <code>mvn clean install</code>.</p> </li> <li> <p>Often, a "simple build" isn&#8217;t sufficient and integration tests are required: <code>mvn clean install -DskipIntegrationTests=false -DincludeNeo4j</code>.</p> </li> <li> <p>Docker can help simplify building and testing: <code>docker/build.sh -t -i -n</code></p> </li> <li> <p>Please see the <a href="#building-testing">Building and Testing</a> section for more building and testing options.</p> </li> </ol> </div> </li> <li> <p>Consider whether documentation or tests need to be added or updated as part of the change, and add them as needed.</p> <div class="olist loweralpha"> <ol class="loweralpha" type="a"> <li> <p>Nearly all changes should include a modification to the <code>CHANGELOG.asciidoc</code> file - one or more entries to help summarize the change.</p> </li> <li> <p>Some changes will require updates to the <a href="https://tinkerpop.apache.org/docs/3.7.3/upgrade/">Upgrade Documentation</a>. Updates to this documentation are usually reserved for major new features and breaking changes.</p> </li> <li> <p>Docker can help simplify documentation generation: <code>docker/build.sh -d</code></p> </li> <li> <p>Please see the <a href="#building-testing">Building and Testing</a> section for more documentation generation options.</p> </li> </ol> </div> </li> <li> <p>Open the <a href="https://help.github.com/articles/using-pull-requests/">pull request</a> against the appropriate branch on the Apache TinkerPop repository.</p> <div class="olist loweralpha"> <ol class="loweralpha" type="a"> <li> <p>Target the pull request at the appropriate branch in TinkerPop&#8217;s repository</p> </li> <li> <p>Prefix the name of the pull request with the JIRA issue number (include a brief description after that).</p> </li> <li> <p>Include a link to the ticket in JIRA in the pull request description.</p> </li> <li> <p>Include a rough synopsis of how the changes were tested. This might be as simple as "Ran mvn clean install to success and performed manual testing in the Gremlin Console".</p> </li> <li> <p>Include other descriptive elements about the change if they are not already included in the JIRA ticket.</p> </li> <li> <p>Automated builds will occur with Travis. Please be sure that the pull request passes those builds and correct them if there are problems.</p> </li> </ol> </div> </li> </ol> </div> <div class="paragraph"> <p>Once the pull request has been placed it will go into <strong>review</strong>:</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p>Other reviewers, including committers, may comment on the changes and suggest modifications. Changes can be added by simply pushing more commits to the same branch.</p> </li> <li> <p>Lively, polite, rapid technical debate is encouraged from everyone in the community. The outcome may be a rejection of the entire change.</p> </li> <li> <p>Reviewers can indicate that a change looks suitable for merging with by providing a "+1". Please see the <a href="#rtc">Review then Commit</a> process for more details.</p> </li> <li> <p>Sometimes, other changes will be merged which conflict with your pull request&#8217;s changes. The PR can&#8217;t be merged until the conflict is resolved. In these cases the PR must be rebased, conflicts resolved and the resulting changes force pushed back to the branch.</p> </li> <li> <p>Try to be responsive to the discussion rather than let days pass between replies.</p> </li> <li> <p>Reviewers may request additional pull requests (e.g. one for each release branch that it may be related to) if the changes brings extensive conflict between branches.</p> </li> </ol> </div> <div class="paragraph"> <p>On successful review, the <strong>pull request will be merged</strong> to the main repository and the JIRA issue will be closed.</p> </div> </div> </div> <div class="sect1"> <h2 id="versioning">Versioning</h2> <div class="sectionbody"> <div class="paragraph"> <p>TinkerPop version numbers follow a format of <code>EPOCH.MAJOR.MINOR.PATCH</code>, where:</p> </div> <div class="ulist"> <ul> <li> <p><code>EPOCH</code> version is incremented for epochs of thinking around TinkerPop and its relation to the graph ecosystem and is part of a complete release.</p> </li> <li> <p><code>MAJOR</code> version is incremented for incompatible changes and is part of a complete release.</p> </li> <li> <p><code>MINOR</code> version is incremented for backward compatible changes and is part of a complete release.</p> </li> <li> <p><code>PATCH</code> version is incremented for backward compatible bug fixes and typically only applied to client modules for partial release.</p> </li> </ul> </div> <div class="paragraph"> <p>The above definitions refer to notions of "complete" and "partial" releases. A complete release is one that releases all TinkerPop components across all languages. A partial release is one that releases just a single module of a driver/client. A partial release is helpful in that it decouples the release of client/driver modules from what are typically server modules. In this way, for example, a small fix can be applied and released around .NET without having to release all the other components which may not have experienced any real change.</p> </div> <div class="paragraph"> <p>The number for <code>EPOCH</code> rarely changes and when it does, it typically represents a complete re-write of the software and thus massive changes can be expected. As a result of the definition for <code>EPOCH</code>, there is greater focus on the other three numbers when considering where changes will land:</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p>A client with a specific <code>EPOCH.MAJOR.MINOR</code> will be backward compatible with user application code written for <code>EPOCH.MAJOR.MINOR</code>.</p> </li> <li> <p>A client with specific <code>EPOCH.MAJOR</code> will be backward compatible with the <code>EPOCH.MAJOR</code> version of the server.</p> </li> </ol> </div> <div class="paragraph"> <p>A complete release is always just <code>EPOCH.MAJOR.MINOR</code> where the <code>PATCH</code> is inferred as "0", thus 3.6.x line would initially release all components as <code>3.6.0</code>. A <code>PATCH</code> number must start with "1" within a release because of how <a href="https://github.com/NuGet/Home/issues/7376">Nuget treats "0"</a>. If it was determined that <code>gremlin-python</code> needed a special release then it could be released independently of the other modules as <code>3.6.0.1</code>.</p> </div> <div class="paragraph"> <p>In short, the dev mailing list is open to discussion on where a change will land. The discussion just needs to have good reasoning and use the general principles above as a framework for coming to a consensus.</p> </div> </div> </div> <h1 id="development-environment" class="sect0">Development Environment</h1> <div class="openblock partintro"> <div class="content"> <div class="paragraph"> <p>TinkerPop is fairly large body of code spread across many modules and covering multiple programming languages. Despite this complexity, it remains relatively straightforward a project to build. This following subsections explain how to configure a development environment for TinkerPop.</p> </div> <div class="paragraph"> <p><span class="image"><img src="../../images/conspiracy.png" alt="conspiracy"></span></p> </div> </div> </div> <div class="sect1"> <h2 id="system-configuration">System Configuration</h2> <div class="sectionbody"> <div class="paragraph"> <p>At a minimum, development of TinkerPop requires <a href="https://openjdk.java.net/projects/jdk8/">Java 8</a> but it is preferable to use <a href="https://openjdk.java.net/projects/jdk/11/">Java 11</a> cross-compiled to Java 8 (the cross-compilation happens automatically as part of the build). Starting with 3.7.0, you can also build with <a href="https://openjdk.org/projects/jdk/17/">Java 17</a>. Note, however, that there are some issues with deep reflection so there are <code>--add-opens</code> JVM options included in the pom files to enable this to work. Maven (requiring a minimum of <a href="https://maven.apache.org/download.cgi">Maven 3.5.3+</a>) is used as the common build system, which even controls the builds of non-JVM <a href="https://tinkerpop.apache.org/docs/3.7.3/reference/#gremlin-drivers-variants">GLVs</a> such as <code>gremlin-python</code>. Java and Maven are described as a "minimum" for a development environment, because they will only build JVM portions of TinkerPop and many integration tests will not fire with this simple setup. It is possible to get a clean and successful build with this minimum, but it will not be possible to build non-JVM aspects of the project and those will go untested.</p> </div> <div class="paragraph"> <p>To gain the ability to execute all aspects of the TinkerPop build system, other environmental configurations must be established. Those prerequisites are defined in the following subsections.</p> </div> <div class="paragraph"> <p>As of TinkerPop 3.5.5, environment configuration for Gremlin Language Variants can be optional, as Docker becomes a system requirement to build and test GLVs inside of Maven. Please make sure Docker is installed and running on your system. You will need to install both <a href="https://docs.docker.com/engine/install/">Docker Engine</a> and <a href="https://docs.docker.com/compose/install/">Docker Compose</a>, which are included in <a href="https://docs.docker.com/desktop/">Docker Desktop</a>.</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> Use Java 11 for documentation generation with <code>bin/process-docs.sh</code> and for other build features outside of the basic <code>mvn clean install</code> sort of function. </td> </tr> </table> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> For those who intend to offer a contribution, building with a minimal configuration may not be sufficient when submitting a pull request. Consider setting up the full environment. </td> </tr> </table> </div> <div class="admonitionblock tip"> <table> <tr> <td class="icon"> <div class="title">Tip</div> </td> <td class="content"> Consider using <a href="https://sdkman.io/">SDKMAN!</a> to manage Java and Maven versions and environments. </td> </tr> </table> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> For those using Windows, efforts have been made to keep the build OS independent, but, in practice, it is likely that TinkerPop&#8217;s build system will only allow for a minimum build at best.<br> <br> Refer to <a href="#building-on-windows">Building On Windows</a> section for more details. </td> </tr> </table> </div> <div class="sect2"> <h3 id="groovy-environment">Groovy Environment</h3> <div class="paragraph"> <p>Groovy is not used in the standard build, but when generating documentation it does require the loading of a Gremlin Console instance. The Gremlin Console is Groovy-based and the documentation bootstrapping loads TinkerPop plugins which requires proper configuration of Graph/Ivy dependency loaders as described in the <a href="https://tinkerpop.apache.org/docs/3.7.3/reference/#gremlin-applications">Gremlin Applications Section</a> of the Reference Documentation.</p> </div> <div class="paragraph"> <p>The base configuration described in that link may need to be modified if there is a desire to work with the Gremlin Console (for documentation generation or just general testing) in a way that utilizes SNAPSHOT releases in the Apache Snapshots Repository. In that case, the <code>grapeConfig.xml</code> will need to include a resolver for that repository and the basic Ivy configuration will look as follows:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;ivysettings&gt;</span> <span class="tag">&lt;settings</span> <span class="attribute-name">defaultResolver</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">downloadGrapes</span><span class="delimiter">&quot;</span></span><span class="tag">/&gt;</span> <span class="tag">&lt;resolvers&gt;</span> <span class="tag">&lt;chain</span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">downloadGrapes</span><span class="delimiter">&quot;</span></span> <span class="attribute-name">returnFirst</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">true</span><span class="delimiter">&quot;</span></span><span class="tag">&gt;</span> <span class="tag">&lt;filesystem</span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">cachedGrapes</span><span class="delimiter">&quot;</span></span><span class="tag">&gt;</span> <span class="tag">&lt;ivy</span> <span class="attribute-name">pattern</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">${user.home}/.groovy/grapes/[organisation]/[module]/ivy-[revision].xml</span><span class="delimiter">&quot;</span></span><span class="tag">/&gt;</span> <span class="tag">&lt;artifact</span> <span class="attribute-name">pattern</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">${user.home}/.groovy/grapes/[organisation]/[module]/[type]s/[artifact]-[revision](-[classifier]).[ext]</span><span class="delimiter">&quot;</span></span><span class="tag">/&gt;</span> <span class="tag">&lt;/filesystem&gt;</span> <span class="tag">&lt;ibiblio</span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">localm2</span><span class="delimiter">&quot;</span></span> <span class="attribute-name">root</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">${user.home.url}/.m2/repository/</span><span class="delimiter">&quot;</span></span> <span class="attribute-name">checkmodified</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">true</span><span class="delimiter">&quot;</span></span> <span class="attribute-name">changingPattern</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">.*</span><span class="delimiter">&quot;</span></span> <span class="attribute-name">changingMatcher</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">regexp</span><span class="delimiter">&quot;</span></span> <span class="attribute-name">m2compatible</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">true</span><span class="delimiter">&quot;</span></span><span class="tag">/&gt;</span> <span class="tag">&lt;ibiblio</span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">jcenter</span><span class="delimiter">&quot;</span></span> <span class="attribute-name">root</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">https://jcenter.bintray.com/</span><span class="delimiter">&quot;</span></span> <span class="attribute-name">m2compatible</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">true</span><span class="delimiter">&quot;</span></span><span class="tag">/&gt;</span> <span class="tag">&lt;ibiblio</span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">ibiblio</span><span class="delimiter">&quot;</span></span> <span class="attribute-name">m2compatible</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">true</span><span class="delimiter">&quot;</span></span><span class="tag">/&gt;</span> <span class="tag">&lt;ibiblio</span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">apache-snapshots</span><span class="delimiter">&quot;</span></span> <span class="attribute-name">root</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">http://repository.apache.org/snapshots/</span><span class="delimiter">&quot;</span></span> <span class="attribute-name">m2compatible</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">true</span><span class="delimiter">&quot;</span></span><span class="tag">/&gt;</span> <span class="tag">&lt;/chain&gt;</span> <span class="tag">&lt;/resolvers&gt;</span> <span class="tag">&lt;/ivysettings&gt;</span></code></pre> </div> </div> <div class="paragraph"> <p>The above configuration is just a modification of the default. Perhaps the most lean common configuration might just be:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;ivysettings&gt;</span> <span class="tag">&lt;settings</span> <span class="attribute-name">defaultResolver</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">downloadGrapes</span><span class="delimiter">&quot;</span></span><span class="tag">/&gt;</span> <span class="tag">&lt;resolvers&gt;</span> <span class="tag">&lt;chain</span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">downloadGrapes</span><span class="delimiter">&quot;</span></span><span class="tag">&gt;</span> <span class="tag">&lt;ibiblio</span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">local</span><span class="delimiter">&quot;</span></span> <span class="attribute-name">root</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">file:${user.home}/.m2/repository/</span><span class="delimiter">&quot;</span></span> <span class="attribute-name">m2compatible</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">true</span><span class="delimiter">&quot;</span></span><span class="tag">/&gt;</span> <span class="tag">&lt;ibiblio</span> <span class="attribute-name">name</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">central</span><span class="delimiter">&quot;</span></span> <span class="attribute-name">root</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">https://repo1.maven.org/maven2/</span><span class="delimiter">&quot;</span></span> <span class="attribute-name">m2compatible</span>=<span class="string"><span class="delimiter">&quot;</span><span class="content">true</span><span class="delimiter">&quot;</span></span><span class="tag">/&gt;</span> <span class="tag">&lt;/chain&gt;</span> <span class="tag">&lt;/resolvers&gt;</span> <span class="tag">&lt;/ivysettings&gt;</span></code></pre> </div> </div> <div class="paragraph"> <p>In the above case, the configuration largely relies on the standard Maven builds to create a well cached <code>.m2</code> directory. Under typical development circumstances, SNAPSHOT will find themselves deployed there locally and that is all that will be required for Grape to do its work.</p> </div> <div class="paragraph"> <p>As a final word, it is important to take note of the order used for these references as Grape will check them in the order they are specified and depending on that order, an artifact other than the one expected may be used which is typically an issue when working with SNAPSHOT dependencies.</p> </div> </div> <div class="sect2"> <h3 id="documentation-environment">Documentation Environment</h3> <div class="paragraph"> <p>The documentation generation process is not Maven-based and uses shell scripts to process the project&#8217;s asciidoc. The scripts should work on Mac and Linux. Javadocs should be built using Java 11.</p> </div> <div class="admonitionblock tip"> <table> <tr> <td class="icon"> <div class="title">Tip</div> </td> <td class="content"> We recommend performing documentation generation on Linux. For the scripts to work on Mac, you will need to install GNU versions of the utility programs via <code>homebrew</code>, e.g.<code>grep</code>, <code>awk</code>, <code>sed</code>, <code>findutils</code>, and <code>diffutils</code>. </td> </tr> </table> </div> <div class="paragraph"> <p>To generate documentation, it is required that <a href="https://hadoop.apache.org">Hadoop 3.3.x</a> is running in <a href="https://hadoop.apache.org/docs/r3.3.1/hadoop-project-dist/hadoop-common/SingleCluster.html#Pseudo-Distributed_Operation">pseudo-distributed</a> mode. Be sure to set the <code>HADOOP_GREMLIN_LIBS</code> environment variable as described in the <a href="https://tinkerpop.apache.org/docs/3.7.3/reference/#hadoop-gremlin">reference documentation</a>. It is also important to set the <code>CLASSPATH</code> to point at the directory containing the Hadoop configuration files, like <code>mapred-site.xml</code>.</p> </div> <div class="paragraph"> <p>The <code>/etc/hadoop/yarn-site.xml</code> file prefers this configuration over the one provided in the Hadoop documentation referenced above:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;configuration&gt;</span> <span class="tag">&lt;property&gt;</span> <span class="tag">&lt;name&gt;</span>yarn.nodemanager.aux-services<span class="tag">&lt;/name&gt;</span> <span class="tag">&lt;value&gt;</span>mapreduce_shuffle<span class="tag">&lt;/value&gt;</span> <span class="tag">&lt;/property&gt;</span> <span class="tag">&lt;property&gt;</span> <span class="tag">&lt;name&gt;</span>yarn.nodemanager.vmem-check-enabled<span class="tag">&lt;/name&gt;</span> <span class="tag">&lt;value&gt;</span>false<span class="tag">&lt;/value&gt;</span> <span class="tag">&lt;/property&gt;</span> <span class="tag">&lt;property&gt;</span> <span class="tag">&lt;name&gt;</span>yarn.nodemanager.vmem-pmem-ratio<span class="tag">&lt;/name&gt;</span> <span class="tag">&lt;value&gt;</span>4<span class="tag">&lt;/value&gt;</span> <span class="tag">&lt;/property&gt;</span> <span class="tag">&lt;/configuration&gt;</span></code></pre> </div> </div> <div class="paragraph"> <p>The <code>/etc/hadoop/mapred-site.xml</code> file prefers the following configuration:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="xml"><span class="tag">&lt;configuration&gt;</span> <span class="tag">&lt;property&gt;</span> <span class="tag">&lt;name&gt;</span>mapreduce.framework.name<span class="tag">&lt;/name&gt;</span> <span class="tag">&lt;value&gt;</span>yarn<span class="tag">&lt;/value&gt;</span> <span class="tag">&lt;/property&gt;</span> <span class="tag">&lt;property&gt;</span> <span class="tag">&lt;name&gt;</span>mapred.map.tasks<span class="tag">&lt;/name&gt;</span> <span class="tag">&lt;value&gt;</span>4<span class="tag">&lt;/value&gt;</span> <span class="tag">&lt;/property&gt;</span> <span class="tag">&lt;property&gt;</span> <span class="tag">&lt;name&gt;</span>mapred.reduce.tasks<span class="tag">&lt;/name&gt;</span> <span class="tag">&lt;value&gt;</span>4<span class="tag">&lt;/value&gt;</span> <span class="tag">&lt;/property&gt;</span> <span class="tag">&lt;property&gt;</span> <span class="tag">&lt;name&gt;</span>mapreduce.job.counters.limit<span class="tag">&lt;/name&gt;</span> <span class="tag">&lt;value&gt;</span>1000<span class="tag">&lt;/value&gt;</span> <span class="tag">&lt;/property&gt;</span> <span class="tag">&lt;property&gt;</span> <span class="tag">&lt;name&gt;</span>mapreduce.jobtracker.address<span class="tag">&lt;/name&gt;</span> <span class="tag">&lt;value&gt;</span>localhost:9001<span class="tag">&lt;/value&gt;</span> <span class="tag">&lt;/property&gt;</span> <span class="tag">&lt;property&gt;</span> <span class="tag">&lt;name&gt;</span>mapreduce.map.memory.mb<span class="tag">&lt;/name&gt;</span> <span class="tag">&lt;value&gt;</span>2048<span class="tag">&lt;/value&gt;</span> <span class="tag">&lt;/property&gt;</span> <span class="tag">&lt;property&gt;</span> <span class="tag">&lt;name&gt;</span>mapreduce.reduce.memory.mb<span class="tag">&lt;/name&gt;</span> <span class="tag">&lt;value&gt;</span>4096<span class="tag">&lt;/value&gt;</span> <span class="tag">&lt;/property&gt;</span> <span class="tag">&lt;property&gt;</span> <span class="tag">&lt;name&gt;</span>mapreduce.map.java.opts<span class="tag">&lt;/name&gt;</span> <span class="tag">&lt;value&gt;</span>-Xmx2048m<span class="tag">&lt;/value&gt;</span> <span class="tag">&lt;/property&gt;</span> <span class="tag">&lt;property&gt;</span> <span class="tag">&lt;name&gt;</span>mapreduce.reduce.java.opts<span class="tag">&lt;/name&gt;</span> <span class="tag">&lt;value&gt;</span>-Xmx4096m<span class="tag">&lt;/value&gt;</span> <span class="tag">&lt;/property&gt;</span> <span class="tag">&lt;/configuration&gt;</span></code></pre> </div> </div> <div class="paragraph"> <p>Also note that <a href="http://www.grymoire.com/Unix/Awk.html">awk</a> version <code>4.0.1</code> is required for documentation generation. The <a href="https://tinkerpop.apache.org/docs/3.7.3/recipes/#olap-spark-yarn">YARN recipe</a> also uses the <code>zip</code> program to create an archive so that needs to be installed, too, if you don&#8217;t have it already.</p> </div> <div class="paragraph"> <p>The Hadoop 3.3.x installation instructions call for installing <code>pdsh</code> but installing that seems to cause permission problems when executing <code>sbin/start-dfs.sh</code>. Skipping that prerequisite seems to solve the problem.</p> </div> <div class="paragraph"> <p>Documentation can be generated locally with:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">bin/process-docs.sh</code></pre> </div> </div> <div class="paragraph"> <p>Documentation is generated to the <code>target/docs</code> directory. It is also possible to generate documentation locally with Docker. <code>docker/build.sh -d</code>.</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> The installation of plugins sometimes fails in this step with the error: <code>Error grabbing grapes - download failed</code>. It often helps in this case to delete the directories for the dependencies that cannot be downloaded in the <code>.m2</code> (<code>~/.m2/</code>) and in the <code>grapes</code> (<code>~/.groovy/grapes/</code>) cache. E.g., if the error is about <code>asm#asm;3.2!asm.jar</code>, then remove the <code>asm/asm</code> sub directory in both directories. </td> </tr> </table> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> Unexpected failures with OLAP often point to a jar conflict that arises in scenarios where Hadoop or Spark dependencies (or other dependencies for that matter) are modified and conflict. It is not picked up by the enforcer plugin because the inconsistency arises through plugin installation in Gremlin Console at document generation time. Making adjustments to the various paths by way of the <code>&lt;manifestEntries&gt;</code> on the jar given the functionality provided by the <code>DependencyGrabber</code> class which allows you to manipulate (typically deleting conflicting files from <code>/lib</code> and <code>/plugin</code>) plugin loading will usually resolve it, though it could also be a more general environmental problem with Spark or Hadoop. The easiest way to see the error is to simply run the examples in the Gremlin Console which more plainly displays the error than the failure of the documentation generation process. </td> </tr> </table> </div> <div class="paragraph"> <p>To generate the web site locally, there is no need for any of the above infrastructure. Site generation is a simple shell script:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">bin/generate-home.sh</code></pre> </div> </div> <div class="paragraph"> <p>The site will be generated to the <code>target/site/home</code> directory.</p> </div> </div> <div class="sect2"> <h3 id="python-environment">Python Environment</h3> <div class="paragraph"> <p>As of TinkerPop 3.2.2, the build optionally requires <a href="https://www.python.org/">Python</a> to build the <code>gremlin-python</code> module. If Python is not installed, TinkerPop will still build with Maven, but native Python tests and Java tests that require Python code will be skipped. Developers should also install <a href="https://pypi.python.org/pypi/pip">pip</a> and <a href="https://virtualenv.pypa.io/en/stable/">virtualenv</a> (version 15.0.2 - older versions may cause build failures).</p> </div> <div class="paragraph"> <p>The build expects Python a <code>python3</code> installation which should be 3.5.3 or better. Python also tests kerberos and therefore requires:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">sudo apt install libkrb5-dev krb5-user</code></pre> </div> </div> <div class="paragraph"> <p>As of TinkerPop 3.5.5, <code>gremlin-python</code> uses Docker for all tests inside of Maven, and Python installation will not be required to run <code>gremlin-python</code> through Maven. Please make sure Docker is installed and running on your system.</p> </div> <div class="paragraph"> <p>Once the Python environment is established, the full building and testing of <code>gremlin-python</code> may commence. It can be done manually from the command line with:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">mvn clean install -Pglv-python</code></pre> </div> </div> <div class="paragraph"> <p>which enables the "glv-python" Maven profile or in a more automated fashion simply add a <code>.glv</code> file to the root of the <code>gremlin-python</code> module which will signify to Maven that the environment is Python-ready. The <code>.glv</code> file need not have any contents and is ignored by Git. A standard <code>mvn clean install</code> will then build <code>gremlin-python</code> in full.</p> </div> <div class="paragraph"> <p>The <code>.glv</code> file in <code>gremlin-python</code> also activates the "console-integration-tests" Maven profile to run gremlin-console integration tests. Alternatively, this profile can be activated manually. Note that unlike <code>gremlin-python</code> the tests are actually integration tests and therefore must be actively switched on with <code>-DskipIntegrationTests=false</code>:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">mvn clean install -pl gremlin-console -DskipIntegrationTests=false</code></pre> </div> </div> <div class="admonitionblock tip"> <table> <tr> <td class="icon"> <div class="title">Tip</div> </td> <td class="content"> For those who do not have a full Maven environment, please see <a href="#docker-integration">this section</a> for how Docker can be used to help run tests. </td> </tr> </table> </div> <div class="admonitionblock tip"> <table> <tr> <td class="icon"> <div class="title">Tip</div> </td> <td class="content"> Consider use of <a href="https://realpython.com/intro-to-pyenv/">pyenv</a> to better manage Python versions and environments. </td> </tr> </table> </div> <div class="paragraph"> <p>See the <a href="#release-environment">Release Environment</a> section for more information on release manager configurations.</p> </div> </div> <div class="sect2"> <h3 id="dotnet-environment">DotNet Environment</h3> <div class="paragraph"> <p>The build optionally requires <a href="https://dotnet.microsoft.com/download">.NET SDK</a> (&gt;=8.0) to work with the <code>gremlin-dotnet</code> module. If .NET SDK is not installed, TinkerPop will still build with Maven, but .NET projects will be skipped.</p> </div> <div class="paragraph"> <p>As of TinkerPop 3.5.5, <code>gremlin-dotnet</code> uses Docker for running all test projects inside of Maven, and .NET SDK will not be required to run <code>gremlin-dotnet</code> tests through Maven. Please make sure Docker is installed and running on your system.</p> </div> <div class="paragraph"> <p><code>gremlin-dotnet</code> can be built and tested from the command line with:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">mvn clean install -Pgremlin-dotnet</code></pre> </div> </div> <div class="paragraph"> <p>which enables the "gremlin-dotnet" Maven profile or in a more automated fashion simply add a <code>.glv</code> file to the <code>src</code> and <code>test</code> directories of the <code>gremlin-dotnet</code> module which will signify to Maven that the environment is .NET-ready. The <code>.glv</code> file need not have any contents and is ignored by Git. A standard <code>mvn clean install</code> will then build <code>gremlin-dotnet</code> in full.</p> </div> <div class="paragraph"> <p>In order to pack the Gremlin.Net.Template project, it is also necessary to install <a href="http://www.mono-project.com/">Mono</a>. The template can still be built and tested without Mono but packing will be skipped. To pack the template (which will also download the <a href="https://docs.microsoft.com/en-us/nuget/tools/nuget-exe-cli-reference">NuGet CLI tool</a>) the <code>nuget</code> property has to be set:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">mvn clean install -Dnuget</code></pre> </div> </div> <div class="admonitionblock tip"> <table> <tr> <td class="icon"> <div class="title">Tip</div> </td> <td class="content"> For those who do not have a full Maven environment, please see <a href="#docker-integration">this section</a> for how Docker can be used to help run tests. </td> </tr> </table> </div> <div class="paragraph"> <p>See the <a href="#release-environment">Release Environment</a> section for more information on release manager configurations.</p> </div> </div> <div class="sect2"> <h3 id="nodejs-environment">JavaScript Environment</h3> <div class="paragraph"> <p>When building <code>gremlin-javascript</code>, mvn command will include a local copy of Node.js runtime and npm inside your project using <code>com.github.eirslett:frontend-maven-plugin</code> plugin. This copy of the Node.js runtime will not affect any other existing Node.js runtime instances in your machine.</p> </div> <div class="paragraph"> <p>To run the development and build scripts of <code>gremlint</code> and its corresponding web page <code>docs/gremlint</code>, Node.js and npm have to be installed. When generating or publishing the TinkerPop website, the <code>docs/gremlint</code> web page has to be built. Consequently, the scripts <code>bin/generate-home.sh</code> and <code>bin/publish-home.sh</code> require that Node.js and npm are installed. Version 8.x or newer of npm is required. This is covered in more detail in the <a href="#site">Site</a> section.</p> </div> <div class="paragraph"> <p>As of TinkerPop 3.5.5, <code>gremlin-javascript</code> uses Docker for all tests inside of Maven. Please make sure Docker is installed and running on your system.</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> Beware of unexpected or unwanted changes on <code>package-lock.json</code> files when committing and merging. </td> </tr> </table> </div> <div class="admonitionblock tip"> <table> <tr> <td class="icon"> <div class="title">Tip</div> </td> <td class="content"> For those who do not have a full Maven environment, please see <a href="#docker-integration">this section</a> for how Docker can be used to help run tests. </td> </tr> </table> </div> <div class="admonitionblock tip"> <table> <tr> <td class="icon"> <div class="title">Tip</div> </td> <td class="content"> Consider using <a href="https://github.com/nvm-sh/nvm">nvm</a> to manage node.js versions and environments. </td> </tr> </table> </div> <div class="paragraph"> <p>See the <a href="#release-environment">Release Environment</a> section for more information on release manager configurations.</p> </div> </div> <div class="sect2"> <h3 id="go-environment">Go Environment</h3> <div class="paragraph"> <p>The build optionally requires <a href="https://go.dev/dl/">Go</a> (&gt;=1.22) to work with the <code>gremlin-go</code> module. Creating an empty <code>.glv</code> file will enable running of tests inside of Maven. If <code>.glv</code> file does not exist, TinkerPop will still build with Maven, but Go projects will be skipped.</p> </div> <div class="paragraph"> <p><code>gremlin-go</code> can be built and tested from the command line with:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">mvn clean install -pl gremlin-go</code></pre> </div> </div> <div class="paragraph"> <p>Alternatively, after installing Go, <code>gremlin-go</code> can be built from the command line with:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">go build</code></pre> </div> </div> <div class="paragraph"> <p>Docker allows you to test the driver without installing any dependencies. The following command can be used to run docker:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">docker compose up --exit-code-from gremlin-go-integration-tests</code></pre> </div> </div> <div class="paragraph"> <p>See the <a href="#release-environment">Release Environment</a> section for more information on release manager configurations.</p> </div> </div> <div class="sect2"> <h3 id="docker-environment">Docker Environment</h3> <div class="paragraph"> <p>The build optionally requires Docker to build Docker images of Gremlin Server and Gremlin Console. The Docker images can be built from the command line with:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">mvn clean install -pl gremlin-server,gremlin-console -DdockerImages</code></pre> </div> </div> <div class="paragraph"> <p>which enables the "docker-images" Maven profile.</p> </div> <div class="paragraph"> <p>If confronted with "Permission denied" errors on Linux, it may be necessary to do the following:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">sudo groupadd docker sudo usermod -aG docker $USER newgrp docker sudo chmod 666 /var/run/docker.sock</code></pre> </div> </div> <div class="paragraph"> <p>As of TinkerPop 3.5.5, a docker image of the Gremlin Server will be built automatically with <code>mvn clean install</code>, which is use for GLV tests inside of Docker. To skip building this image, append the <code>-DskipImageBuild</code> flag to Maven commands.</p> </div> </div> <div class="sect2"> <h3 id="release-environment">Release Environment</h3> <div class="paragraph"> <p>This section is only useful to TinkerPop release managers and describes prerequisites related to deploying an official release of TinkerPop.</p> </div> <div class="paragraph"> <p>Maven needs to be configured to deploy maven artifacts. Apache LDAP credentials can be used for this. Release managers should encrypt their Apache LDAP password as described <a href="https://maven.apache.org/guides/mini/guide-encryption.html">in the Apache Maven docs</a>. The encrypted password can then be configured in the <code>settings.xml</code> as described in the section <a href="https://infra.apache.org/publishing-maven-artifacts.html">"Set up your development environment" of this Apache Infra article</a>. This configuration will be used by <code>mvn deploy</code>.</p> </div> <div class="paragraph"> <p>For Python releases, uploading to pypi uses <a href="https://pypi.python.org/pypi/twine">twine</a> which is automatically installed by the build process in maven. Twine refers to <code>HOME/.pypirc</code> file for configuration on the pypi deploy environments and username and password combinations. The file typically looks like this:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">[distutils] index-servers= pypi pypitest [pypitest] username = &lt;username&gt; password = [pypi] username = &lt;username&gt; password =</code></pre> </div> </div> <div class="paragraph"> <p>The approach above assumes basic authentication and a blank password will in <code>.pyirc</code> will have Maven prompt for a password. That said, it is preferred that your account be secured with 2FA, in which case, basic authentication will no longer be allowed. Instead, an API token is required. It can be generated under your <a href="https://pypi.org/manage/account/token/">account settings</a> and then the username in <code>.pyirc</code> is set to <code><em>token</em></code> and the password to the token value. Please see <a href="https://pypi.org/help/#apitoken">PyPI documentation</a> for more details.</p> </div> <div class="paragraph"> <p>For .NET releases, install <a href="http://www.mono-project.com/">Mono</a>. The release process is known to work with 6.12.0, so it is best to probably install that version. Release managers should probably also do an install of <a href="https://dist.nuget.org/win-x86-commandline/v3.4.4/nuget.exe">nuget 3.4.4</a> as it will help with environmental setup. To get an environment ready to deploy to NuGet, it is necessary to have a NuGet API key. First, create an account with <a href="https://www.nuget.org">nuget</a> and request that a PMC member add your account to the Gremlin.Net and the Gremlin.Net.Template package in nuget so that you can deploy. Next, generate an API key for your account on the nuget website. The API key should be added to <code>NuGet.Config</code> with the following:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">mono nuget.exe setApiKey [your-api-key]</code></pre> </div> </div> <div class="paragraph"> <p>This should update <code>~/.config/NuGet/NuGet.Config</code> a file with an entry containing the encrypted API key. On <code>mvn deploy</code>, this file will be referenced on the automated <code>nuget push</code>.</p> </div> <div class="paragraph"> <p>To deploy JavaScript / TypeScript artifacts on the <a href="https://www.npmjs.com">npm registry</a>, the release manager must set the authentication information on the ~/.npmrc file. The easiest way to do that is to use the <code>npm adduser</code> command. This must be done only once, as the auth token doesn&#8217;t have an expiration date and it&#8217;s stored on your file system. If this account is newly created then request that a PMC member add your account to the "gremlin" package on npm.</p> </div> <div class="paragraph"> <p>Deploying Docker images to <a href="https://hub.docker.com/">Docker Hub</a> requires an account that is a member of the TinkerPop organization. So if you don&#8217;t already have an account on Docker Hub then create one and request that a PMC member adds your account to the TinkerPop organization. Afterwards, authentication information needs to be added to the <code>~/.docker/config.json</code> file. This information can simply be added with the <code>docker login</code> command which will ask for credentials. This must be done only once. Finally, <code>docker push</code> can be used to push images to Docker Hub which will be done automatically on <code>mvn deploy</code> or it can be triggered manually with <code>mvn docker:push</code>.</p> </div> </div> </div> </div> <div class="sect1"> <h2 id="building-testing">Building and Testing</h2> <div class="sectionbody"> <div class="paragraph"> <p>The following commands are a mix of Maven flags and shell scripts that handle different build operations</p> </div> <div class="ulist"> <ul> <li> <p>Build project: <code>mvn clean install</code></p> <div class="ulist"> <ul> <li> <p>Build a specific module (e.g. <code>gremlin-server</code>) within the project: <code>mvn clean install -pl gremlin-server</code></p> </li> <li> <p>Build without assertions for "iterator leaks" which are enabled by default: <code>mvn clean install -DtestIteratorLeaks=false</code></p> </li> <li> <p>Specify specific tests in a TinkerPop Suite to run with the <code>GREMLIN_TESTS</code> environment variable, along with the Maven project list argument, e.g.:</p> <div class="listingblock"> <div class="content"> <pre>export GREMLIN_TESTS='org.apache.tinkerpop.gremlin.process.traversal.step.map.PathTest$Traversals,org.apache.tinkerpop.gremlin.process.traversal.PathTest' mvn -Dmaven.javadoc.skip=true --projects tinkergraph-gremlin test</pre> </div> </div> </li> <li> <p>Clean the <code>.groovy/grapes/org.apache.tinkerpop</code> directory on build: <code>mvn clean install -DcleanGrapes</code></p> </li> <li> <p>Turn off "heavy" logging in the "process" tests: <code>mvn clean install -DargLine="-DmuteTestLogs=true"</code></p> </li> <li> <p>The test suite for <code>neo4j-gremlin</code> is disabled by default - to turn it on: <code>mvn clean install -DincludeNeo4j</code></p> </li> </ul> </div> </li> <li> <p>Regenerate toy graph data (only necessary given changes to IO classes): <code>mvn clean install -Dio</code> from <code>tinkergraph-gremlin</code> directory</p> <div class="ulist"> <ul> <li> <p>If there are changes to the Gryo format, it may be necessary to generate the Grateful Dead dataset from GraphSON (see <code>IoDataGenerationTest.shouldWriteGratefulDead</code>)</p> </li> </ul> </div> </li> <li> <p>Start Gremlin Server with Docker using the standard test configuration: <code>docker/gremlin-server.sh</code></p> </li> <li> <p>Check license headers are present: <code>mvn apache-rat:check</code></p> </li> <li> <p>Build AsciiDocs (see <a href="#documentation-environment">Documentation Environment</a>): <code>bin/process-docs.sh</code></p> <div class="ulist"> <ul> <li> <p>Build AsciiDocs (but don&#8217;t evaluate code blocks): <code>bin/process-docs.sh --dryRun</code></p> </li> <li> <p>Build AsciiDocs (but don&#8217;t evaluate code blocks in specific files): <code>bin/process-docs.sh --dryRun docs/src/reference/the-graph.asciidoc,docs/src/tutorial/getting-started,&#8230;&#8203;</code></p> </li> <li> <p>Build AsciiDocs (but evaluate code blocks only in specific files): <code>bin/process-docs.sh --fullRun docs/src/reference/the-graph.asciidoc,docs/src/tutorial/getting-started,&#8230;&#8203;</code></p> </li> <li> <p>Process a single AsciiDoc file: docs/preprocessor/preprocess-file.sh `pwd`/gremlin-console/target/apache-tinkerpop-gremlin-console-*-standalone "" "*" `pwd`/docs/src/xyz.asciidoc</p> </li> </ul> </div> </li> <li> <p>Build JavaDocs/JSDoc: <code>mvn process-resources -Djavadoc</code></p> <div class="ulist"> <ul> <li> <p>Javadoc to <code>target/site/apidocs</code> directory</p> </li> <li> <p>JSDoc to the <code>gremlin-javascript/src/main/javascript/gremlin-javascript/doc/</code> directory</p> </li> </ul> </div> </li> <li> <p>Specify the seed used for <code>Random</code> in tests <code>mvn clean install -DtestSeed</code> - useful when a test fails, the seed will be printed in the build output so that the test can run with the same version of random (look for "TestHelper" logger in output)</p> </li> <li> <p>Check for newer dependencies: <code>mvn versions:display-dependency-updates</code> or <code>mvn versions:display-plugin-updates</code></p> </li> <li> <p>Check the effective <code>pom.xml</code>: <code>mvn -pl gremlin-python -Pglv-python help:effective-pom -Doutput=withProfilePom.xml</code></p> </li> <li> <p>Deploy JavaDocs/AsciiDocs: <code>bin/publish-docs.sh svn-username</code></p> </li> <li> <p>Integration Tests: <code>mvn verify -DskipIntegrationTests=false</code></p> <div class="ulist"> <ul> <li> <p>Execute with the <code>-DincludeNeo4j</code> option to include neo4j-gremlin tests. As of 3.7.0, transactional tests on Gremlin Server, Gremlin Driver, and GLVs will run automatically with integration tests against the TinkerTransactionGraph.</p> </li> <li> <p>Execute with the <code>-DuseEpoll</code> option to try to use Netty native transport (works on Linux, but will fallback to Java NIO on other OS).</p> </li> </ul> </div> </li> <li> <p>Benchmarks: <code>mvn verify -DskipBenchmarks=false</code></p> <div class="ulist"> <ul> <li> <p>Reports are generated to the console and to <code>gremlin-tools/gremlin-benchmark/target/reports/benchmark</code>.</p> </li> </ul> </div> </li> <li> <p>Test coverage report: <code>mvn clean install -Dcoverage</code> - note that the <code>install</code> is necessary because report aggregation is bound to that part of the lifecycle.</p> <div class="ulist"> <ul> <li> <p>Reports are generated to <code>gremlin-tools/gremlin-coverage/target/site</code>.</p> </li> </ul> </div> </li> <li> <p><code>cd site</code></p> <div class="ulist"> <ul> <li> <p>Generate web site locally: <code>bin/generate-home.sh</code></p> </li> <li> <p>Publish web site: <code>bin/publish-home.sh &lt;username&gt;</code></p> </li> </ul> </div> </li> </ul> </div> </div> </div> <div class="sect1"> <h2 id="building-on-windows">Building On Windows</h2> <div class="sectionbody"> <div class="paragraph"> <p>The following steps must be taken in order to build TinkerPop on Windows:</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p>Install winutils for Hadoop</p> <div class="ulist"> <ul> <li> <p>Download the latest version of <a href="https://github.com/kontext-tech/winutils">winutils</a> binaries for Hadoop. The binaries <code>winutils.exe</code> and <code>hadoop.dll</code> are required.</p> <div class="ulist"> <ul> <li> <p>Note that these libraries require Microsoft Visual C Redistributable 2015-2022 to be installed. We've tested it on Windows 10 and Windows 11 with Microsoft Visual C Redistributable 2015-2022 14.32.31326.</p> </li> </ul> </div> </li> <li> <p>Place contents of the bin folder on your local driver in the following folder structure:</p> <div class="ulist"> <ul> <li> <p>e.g. <code>hadoop-3.3.1/bin/winutils.exe</code></p> </li> </ul> </div> </li> <li> <p>Set <code>HADOOP_HOME</code> to point to the <code>hadoop-3.3.1</code> folder</p> </li> <li> <p>Add <code>%HADOOP_HOME%\bin</code> to your <code>PATH</code></p> </li> </ul> </div> </li> <li> <p>Run <code>mvn clean install</code> from root of tinkerpop</p> </li> <li> <p>Follow IDE specific steps if applicable:</p> <div class="ulist"> <ul> <li> <p><a href="#intellij">Intellij Usage</a></p> </li> </ul> </div> </li> </ol> </div> <div class="paragraph"> <p>You should now be able to work with TinkerPop on Windows.</p> </div> </div> </div> <div class="sect1"> <h2 id="docker-integration">Docker Integration</h2> <div class="sectionbody"> <div class="paragraph"> <p>TinkerPop provides a shell script, that can start several build tasks within a Docker container. The required Docker images will be built automatically if they don&#8217;t exist yet. Thus the first invocation of the Docker script is expected to take some time.</p> </div> <div class="paragraph"> <p>The script can be found under <code>PROJECT_HOME/docker/build.sh</code>. The following tasks are currently supported:</p> </div> <div class="ulist"> <ul> <li> <p>run standard test suite</p> </li> <li> <p>run integration tests</p> </li> <li> <p>build Java docs</p> </li> <li> <p>build user docs</p> </li> </ul> </div> <div class="paragraph"> <p>A list of command line options is provided by <code>docker/build.sh --help</code>. The container will install, configure and start all required dependencies, such as Hadoop.</p> </div> <div class="paragraph"> <p>By default, this script will run every module in the project. However, if you are planning on working on just a small set of the modules (e.g. the GLVs) then you can use the script options to reduce the modules included by specifically selecting which modules you want. This behavior is currently supported for the non-Java GLVs and gremlin-console. This option will include only the selected modules as well as gremlin-server, gremlin-test, neo4j-gremlin and all their dependencies. This is the minimum set of modules required to build and test the GLVs.</p> </div> <div class="listingblock"> <div class="title">Build and test gremlin-python and gremlin-go with minimal Gremlin Server dependencies</div> <div class="content"> <pre class="CodeRay highlight"><code data-lang="bash">./docker/build.sh --tests --integration-tests --python --golang</code></pre> </div> </div> <div class="paragraph"> <p>Options can be passed to Docker by setting the <code>TINKERPOP_DOCKER_OPTS</code> environment variable. A speed boost can be gained at the expense of memory by using tmpfs and the special directory <code>/usr/src/tinkermem</code>.</p> </div> <div class="listingblock"> <div class="title">Build in-memory</div> <div class="content"> <pre class="CodeRay highlight"><code data-lang="bash">TINKERPOP_DOCKER_OPTS=&quot;--tmpfs /usr/src/tinkermem:exec,mode=0755,rw,noatime,size=2000m&quot;</code></pre> </div> </div> <div class="listingblock"> <div class="title">Disable IPv6 for Hadoop</div> <div class="content"> <pre class="CodeRay highlight"><code data-lang="bash">TINKERPOP_DOCKER_OPTS=&quot;--sysctl net.ipv6.conf.all.disable_ipv6=1 --sysctl net.ipv6.conf.default.disable_ipv6=1&quot;</code></pre> </div> </div> <div class="paragraph"> <p>A custom maven settings.xml can be supplied, for example, to point to a local proxy. Copy the <code>settings.xml</code> to the <code>PROJECT_HOME/</code> directory. The Docker script will detect and copy it to the running container.</p> </div> <div class="paragraph"> <p>If the container is used to generate the user docs, it will start a web server and show the URL that is used to host the HTML docs.</p> </div> <div class="paragraph"> <p>After finishing all tasks, the script will immediately destroy the container.</p> </div> <div class="paragraph"> <p>Docker can also be helpful to developers who do not want to run tests from a Maven environment, which may be a bit opaque when dealing with test failures and largely unhelpful for debugging. This situation is typically case for developers doing work on Gremlin Language Variants (e.g. Python). To help alleviate this problem, developers can start a standalone Gremlin Server with its standard test configuration that is used in the standard Maven build.</p> </div> <div class="paragraph"> <p>Generally speaking, most developers will want to test their code against the latest build of Gremlin Server in the TinkerPop repository. To do that, first be sure to build a Docker image of the current code:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="bash">mvn clean install -DskipTests</code></pre> </div> </div> <div class="paragraph"> <p>Next, generate the a Docker image for Gremlin Server with:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="bash">mvn clean install -pl :gremlin-server -DdockerImages -DskipTests</code></pre> </div> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> If changes are made to the repository that need to be reflected in the Gremlin Server Docker image then the old image should be removed and then the above commands re-executed. </td> </tr> </table> </div> <div class="paragraph"> <p>Finally, start the server with:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="bash">docker/gremlin-server.sh</code></pre> </div> </div> <div class="paragraph"> <p>Starting Gremlin Server this way makes it possible to run Gremlin Language Variant tests without Maven (for example, directly from a debugger) which should greatly reduce development friction for these environments.</p> </div> <div class="paragraph"> <p>It is also possible to specify the exact version of Gremlin Server to run with the test configuration. This version should be an existing Docker image version and must be an explicit version that maps to an actual TinkerPop artifact:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="bash">docker/gremlin-server.sh 3.4.2</code></pre> </div> </div> <div class="paragraph"> <p>To be a bit more clear, the version can not be a Docker tag like "latest" because there is no such TinkerPop artifact that has been published with that version number.</p> </div> </div> </div> <div class="sect1"> <h2 id="docker-testing">Testing Sub-Modules with Docker</h2> <div class="sectionbody"> <div class="paragraph"> <p>Currently the modules gremlin-go, gremlin-javascript, gremlin-dotnet, gremlin-python and gremlin-console can be tested through Docker.</p> </div> <div class="paragraph"> <p>Please make sure Docker is installed and running on your system. You will need to install both <a href="https://docs.docker.com/engine/install/">Docker Engine</a> and <a href="https://docs.docker.com/compose/install/">Docker Compose</a>, which are included in <a href="https://docs.docker.com/desktop/">Docker Desktop</a>.</p> </div> <div class="paragraph"> <p>The following environment variables used by Docker Compose will automatically be set when running through Maven.</p> </div> <div class="paragraph"> <p>The docker compose environment variable <code>GREMLIN_SERVER</code> specifies the Gremlin server docker image to use, i.e. an image with the tag <code>tinkerpop/gremlin-server:$GREMLIN_SERVER</code>, and is a required environment variable. This also requires the specified docker image to exist, either locally or in <a href="https://hub.docker.com/r/tinkerpop/gremlin-server">Docker Hub</a>.</p> </div> <div class="paragraph"> <p>Running <code>mvn clean install -pl gremlin-server -DskipTests -DskipIntegrationTests=true -Dci -am</code> in the main <code>tinkerpop</code> directory will automatically build a local SNAPSHOT Gremlin server image. If your OS Platform cannot build a local SNAPSHOT Gremlin server through <code>maven</code>, it is recommended to use the latest released server version from <a href="https://hub.docker.com/r/tinkerpop/gremlin-server">Docker Hub</a> (do not use <code>GREMLIN_SERVER=latest</code>, use actual version number, e.g. <code>GREMLIN_SERVER=3.5.x</code> or <code>GREMLIN_SERVER=3.6.x</code>).</p> </div> <div class="paragraph"> <p>The docker compose environment variable <code>HOME</code> specifies the user home directory for mounting volumes during test image set up. This variable is set by default in Unix/Linux, but will need to be set for Windows, for example, run <code>$env:HOME=$env:USERPROFILE</code> in PowerShell.</p> </div> <div class="paragraph"> <p>There are different ways to launch the test suite and set the <code>GREMLIN_SERVER</code> environment variable depending on your Platform:</p> </div> <div class="ulist"> <ul> <li> <p>Run Maven commands, e.g. <code>mvn clean install</code> inside of project folder e.g. <code>tinkerpop/gremlin-go</code>, or <code>mvn clean install -pl gremlin-go</code> inside of <code>tinkerpop</code> (platform-agnostic - recommended)</p> </li> <li> <p>Add <code>GREMLIN_SERVER=&lt;server-image-version&gt;</code> and <code>HOME=&lt;user-home-directory&gt;</code> to an <code>.env</code> file inside project folder and run <code>docker compose up --exit-code-from gremlin-go-integration-tests</code> (Platform-agnostic).</p> </li> <li> <p>Run <code>GREMLIN_SERVER=&lt;server-image-version&gt; docker compose up --exit-code-from gremlin-go-integration-tests</code> in Unix/Linux.</p> </li> <li> <p>Run <code>$env:GREMLIN_SERVER="&lt;server-image-version&gt;";$env:HOME=$env:USERPROFILE;docker compose up --exit-code-from gremlin-go-integration-tests</code> in Windows PowerShell.</p> </li> </ul> </div> <div class="paragraph"> <p>You should see exit code 0 upon successful completion of the test suites. Run <code>docker compose down</code> to remove the service containers (not needed if you executed Maven commands or <code>run.sh</code>), or <code>docker compose down --rmi all</code> to remove the service containers while deleting all used images.</p> </div> <div class="paragraph"> <p>Note for running docker with MacOS on ARM processors: Docker&#8217;s performance is extremely poor on ARM Mac&#8217;s in its default configuration. It is recommended to enable both the "New Virtualization Framework" and "VirtioFS" under Docker Desktop Settings &#8594; Experimental Features.</p> </div> </div> </div> <div class="sect1"> <h2 id="intellij">Intellij Usage</h2> <div class="sectionbody"> <div class="paragraph"> <p>Most core TinkerPop developers are using Intellij for their work so this section helps describe the mechanisms for best working with it as an IDE.</p> </div> <div class="sect2"> <h3 id="_setup">Setup</h3> <div class="paragraph"> <p>Installation and basic configuration of Intellij is beyond the scope of this writing and it is assumed that the TinkerPop GitHub repository has been cloned and the root of the repository is open in Intellij. From there, we can begin to look at configuration options specifically relevant to TinkerPop itself.</p> </div> <div class="paragraph"> <p>TinkerPop has a module called <code>gremlin-shaded</code> which contains shaded dependencies for some libraries that are widely used and tend to introduce conflicts. To ensure that Intellij properly interprets this module after importing the Maven <code>pom.xml</code> perform the following steps:</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p>Build <code>gremlin-shaded</code> from the command line with <code>mvn clean install</code>.</p> </li> <li> <p>Right-click on the <code>gremlin-shaded</code> module in the project viewer of Intellij and select "Remove module". If this menu option is not available (as is the case in newer versions of Intellij - first noticed in 13.1.5), then open the "Maven Projects" side panel, right click the <code>gremlin-shaded</code> module and select "Ignore Project".</p> </li> <li> <p>In the "Maven Projects" Tool window and click the tool button for "Reimport All Maven projects" (go to <code>View | Tool Windows | Maven Projects</code> on the main menu if this panel is not activated).</p> </li> <li> <p>At this point it should be possible to compile and run the tests within Intellij, but in the worst case, use <code>File | Invalidate Caches/Restart</code> to ensure that indices properly rebuild.</p> </li> </ol> </div> <div class="paragraph"> <p>Note that it may be necessary to re-execute these steps if the <code>gremlin-shaded</code> <code>pom.xml</code> is ever updated.</p> </div> <div class="paragraph"> <p>You will initially see lots of errors related to the Gremlin lexer/parser. The <code>gremlin-language</code> module requires ANTLR processing. While this processing is configured to execute with Maven, it can also be setup to generate parser files within Intellij itself on command:</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p>Install the ANTLR4 Grammar Plugin for Intellij</p> </li> <li> <p>Right-click on the <code>Gremlin.g4</code> file and "Configure ANTLR"</p> </li> <li> <p>Set "Output directory where all output is generated" to <code>target/generated-sources/antlr4</code></p> </li> <li> <p>Set "Grammar file encoding" to <code>utf-8</code></p> </li> <li> <p>Set "Package/namespace for the generated code" to <code>org.apache.tinkerpop.gremlin.language.grammar</code></p> </li> <li> <p>Set "Language" to <code>Java</code></p> </li> <li> <p>Set "Case transformation in the Preview window" should be "Leave as-is"</p> </li> <li> <p>The "generate parse tree listener" should be unchecked and the "generate parse tree visitor" should be checked.</p> </li> </ol> </div> <div class="paragraph"> <p>With these settings it should be possible to right-click <code>Gremlin.g4</code> and "Generate ANTLR Recognizer" which will place the generated code in where specified at <code>target/generated-sources/antlr4</code>. Be sure to right-click the <code>antlr4</code> directory and "Mark directory as" "Generated Sources Root" which should allow Intellij to recognize it.</p> </div> <div class="paragraph"> <p>The <code>gremlin-groovy</code> module uses a Java annotation processor to help support DSLs. Annotation processing in Intellij should be set up by the Maven import, but if this is set up incorrectly you will see a cryptic error message when building: <code>java: Compilation failed: internal java compiler error</code>. To fix this, search for the Intellij setting <code>Annotation Processors</code>. Make sure that annotation processing is enabled for <code>gremlin-groovy</code> but disabled for <code>gremlin-annotations</code>. This should fix the internal java compile error. Next you may see compile errors complaining that the <code>jsr223</code> classes <code>__</code>, <code>`CredentialTraversal</code>, <code>CredentialTraversalSource</code>, and <code>DefaultCredentialTraversal</code> cannot be found. Those classes are generated by annotations. To fix these errors, make sure mark the directory <code>gremlin-groovy/target/generated-sources/annotations</code> as "Generated Sources Root".</p> </div> <div class="paragraph"> <p>Developers working on the <code>neo4j-gremlin</code> module should enabled the <code>include-neo4j</code> Maven profile in Intellij. This will ensure that tests will properly execute within the IDE.</p> </div> </div> <div class="sect2"> <h3 id="_debugging">Debugging</h3> <div class="paragraph"> <p>It is generally assumed that JVM-based debugging of TinkerPop code in Intellij is a relatively straightforward task for most developers, but it is worth pointing out a few important points related to it and to drill into some specifics for the non-JVM languages.</p> </div> <div class="sect3"> <h4 id="debug-java">Java</h4> <div class="paragraph"> <p>There are generally no complexities to running the debugger for any JVM-based test in the repository, but the following tips are helpful to know when doing so:</p> </div> <div class="ulist"> <ul> <li> <p>It is not possible to run the tests in <code>gremlin-test</code> without an <code>Graph</code> implementation. For example, it is not possible to just right-click <code>org.apache.tinkerpop.gremlin.process.traversal.step.filter.CoinTest</code> and then select <code>Debug 'CoinTest'</code>. Instead, running that test would require opening <code>tinkergraph-gremlin</code> and executing the <code>TinkerGraphProcessStandardTest</code> (as an example) which runs the full Gremlin test suite to include <code>CoinTest</code>.</p> </li> <li> <p>To run just <code>CoinTest</code>, set a <code>GREMLIN_TESTS</code> environment variable with the fully qualified path name to the test configuration in Intellij. Note that the fully qualified test name is really <code>org.apache.tinkerpop.gremlin.process.traversal.step.filter.CoinTest$Traversals</code>.</p> </li> <li> <p>Gherkin tests have a similar pattern in that they require a <code>Graph</code> implementation to execute them. Therefore, debugging entails going to <code>tinkergraph-gremlin</code> and running <code>TinkerGraphFeatureTest</code> in the debugger.</p> </li> <li> <p>It is possible to filter the Gherkin tests by adding a system property to the debug configuration that specifies the tags to use or ignore. For example to just run the <code>coin()</code> tests: <code>-Dcucumber.filter.tags="@StepCoin"</code></p> </li> </ul> </div> </div> <div class="sect3"> <h4 id="debug-python">Python</h4> <div class="paragraph"> <p>Debugging Python within this mainly JVM-based project structure requires a bit of configuration. The following steps will help get Intellij prepared for this task:</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p>Install the Python plugin from JetBrains which should provide PyCharm like functionality.</p> </li> <li> <p>Right-click the "tinkerpop" top-level module in Intellij&#8217;s project explorer and "Open Module Settings".</p> </li> <li> <p>Select "Platform Settings | SDKs" and then click the "+" to "Add Python SDK&#8230;&#8203;"</p> </li> <li> <p>Choose a "Virtualenv Environment" and a "New environment". Set the "Location" to "&lt;project-root&gt;/gremlin-python/src/main/python". Select a "Base interpreter" that matches the version required by <code>gremlin-python</code> and click "OK".</p> </li> <li> <p>Select "Project Settings | Modules" and then select "gremlin-python" from the listing. Change the "Module SDK" to the newly added Python SDK.</p> </li> <li> <p>Open a terminal to <code>gremlin-python/scr/main/python</code> and do <code>venv/bin/pip3 install -e .</code> to pull in all of the <code>gremlin-python</code> dependencies.</p> </li> </ol> </div> <div class="paragraph"> <p>At this stage, it should be possible to run unit tests in Python:</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p>Pull down the drop down in the toolbar for "Run/Debug Configurations" and select "Edit Configurations&#8230;&#8203;"</p> </li> <li> <p>Click the "+" to "Python Tests | pytest" and browse to a test to supply the "Script path".</p> </li> <li> <p>Go to "Python interpreter" and select "Use SDK of module" and choose "gremlin-python" in the drop-down and click OK.</p> </li> <li> <p>The test should not appear in the "Run/Debug Configurations" and can be executed.</p> </li> </ol> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> When the Debug button is pressed, Intellij may display a notification that using the debugger requires some additional downloads - confirm those installations as required. </td> </tr> </table> </div> <div class="paragraph"> <p>Many of the tests in <code>gremlin-python</code> require Gremlin Server. They are effectively integration tests. TinkerPop makes it easy to debug these tests by providing a Docker based test server which is rigged up with all the configurations required for the tests to execute. Start this server with <code>docker/gremlin-server.sh -n</code> where the <code>-n</code> will enable Neo4j for transaction based tests.</p> </div> </div> </div> </div> </div> <h1 id="_for_committers" class="sect0">For Committers</h1> <div class="openblock partintro"> <div class="content"> <div class="imageblock"> <div class="content"> <img src="../../images/business-gremlin.png" alt="business gremlin" width="400"> </div> </div> <div class="paragraph"> <p>The guidelines that follow generally apply to those with commit access to the main repository, but those seeking to contribute will also find helpful information here on the development style and process for the project.</p> </div> </div> </div> <div class="sect1"> <h2 id="initial-setup">Initial Setup</h2> <div class="sectionbody"> <div class="paragraph"> <p>Once the Apache TinkerPop PMC has sent an invitation to a contributor to become a new committer and that contributor has accepted that invitation and provided their iCLA to Apache, then there are some administrative steps that the new committer can expect to go through to complete the process so that they have access to Apache resources like the Git repository. While the information for completing the process can be found in a multitude of places the following listing provides a summary of what to do next:</p> </div> <div class="ulist"> <ul> <li> <p>Look for a welcome email from root_at_apache.org. This will contain your Apache user name</p> <div class="ulist"> <ul> <li> <p>e.g. "Welcome to the Apache Software Foundation (ASF)!"</p> </li> </ul> </div> </li> <li> <p>Visit <a href="https://id.apache.org/reset/enter" class="bare">https://id.apache.org/reset/enter</a> and enter your user name</p> </li> <li> <p>Look for a password reset email from root_at_apache.org. This will have been sent after you confirmed your user name, above.</p> <div class="ulist"> <ul> <li> <p>e.g. "Password reset request for [username] from Apache ID"</p> </li> </ul> </div> </li> <li> <p>Visit the link provided in the password reset email, and choose a new password. ASF asks you to choose a strong one. You will see a "Password change successful" page when this is done.</p> </li> <li> <p>You will now have SVN access</p> <div class="ulist"> <ul> <li> <p><a href="https://svn.apache.org/">svn.apache.org</a></p> </li> </ul> </div> </li> <li> <p>Try sending yourself an email at your new [username]@apache.org email address. It should forward to your primary address.</p> </li> <li> <p>Check your account details at <a href="https://id.apache.org/details/">username</a></p> </li> <li> <p>Link your accounts using gitbox (<a href="https://gitbox.apache.org/setup/" class="bare">https://gitbox.apache.org/setup/</a>)</p> <div class="ulist"> <ul> <li> <p>Link your Apache account</p> </li> <li> <p>Link your GitHub account</p> <div class="ulist"> <ul> <li> <p>You will be asked to "Authorize Apache M.A.T.T."</p> </li> </ul> </div> </li> <li> <p>Obtain MFA status</p> <div class="ulist"> <ul> <li> <p>Visit <a href="https://id.apache.org">id.apache.org</a> and set your GitHub ID to be invited to the org</p> </li> <li> <p>Wait for an email to arrive from "support_at_github.com", and click "Join @apache". Accept the invitation on github.com. Your new MFA status will not immediately be reflected on github, but you will get a confirmation email from noreply_at_github.com. Later, the status will read "MFA ENABLED"</p> <div class="ulist"> <ul> <li> <p>e.g. "[GitHub] @asf-gitbox has invited you to join the @apache organization"</p> </li> </ul> </div> </li> <li> <p>You can find yourself by searching in the <a href="https://github.com/orgs/apache/teams/apache-committers">Apache Committers listing</a></p> </li> </ul> </div> </li> </ul> </div> </li> <li> <p>Read the <a href="https://www.apache.org/dev/new-committers-guide.html">Apache Committer Guide</a> and <a href="http://www.apache.org/dev/committers.html">Apache Committer FAQ</a></p> </li> <li> <p>Read through the other sections of this document - the Developer Documentation - for more details on project procedures and other administrative items.</p> <div class="ulist"> <ul> <li> <p>In particular, see <a href="#rtc">Review then Commit</a></p> </li> </ul> </div> </li> <li> <p>If you have trouble committing, email <a href="mailto:dev@tinkerpop.apache.org">dev@tinkerpop.apache.org</a></p> </li> </ul> </div> </div> </div> <div class="sect1"> <h2 id="_communication">Communication</h2> <div class="sectionbody"> <div class="paragraph"> <p>TinkerPop has a <a href="http://groups.google.com/group/gremlin-users">user mailing list</a> and a <a href="https://lists.apache.org/list.html?dev@tinkerpop.apache.org">dev mailing list</a>. As a committer, it is a good idea to join both.</p> </div> <div class="paragraph"> <p>TinkerPop also has a <a href="https://the-asf.slack.com/archives/CUBJ577EW">Slack channel</a> for more real-time communication about the project among contributors, though it must be kept in mind that project discussion and decisions must occur on the dev mailing list mentioned above.</p> </div> <div class="paragraph"> <p>Occasionally, online meetings via video conference are held. These meetings are schedule via the dev mailing list about a week before they are to occur to find a day and time that is available for those interested in attending. On the day of the meeting, the meeting organizer will create a Google Hangout (or similar video conferencing link). At that point, all who are interested can attend. Meeting minutes should be taken and added to the <a href="#meetings">Meetings</a> section of this document using the pattern already established.</p> </div> </div> </div> <div class="sect1"> <h2 id="_media_content_submission">Media Content Submission</h2> <div class="sectionbody"> <div class="paragraph"> <p>Tinkerpop hosts <a href="https://www.twitch.tv/apachetinkerpop">Twitch streams</a> for various knowledge sharing sessions and demos, as well as a <a href="https://www.youtube.com/@apachetinkerpop">YouTube channel</a> to upload video content. Everyone in the community is welcome to submit contents to stream or upload, abiding the following process:</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p>Send a DISCUSS thread to the dev mailing list that describes the topic, the platform it goes to, who will present it and a date to do it.</p> </li> <li> <p>If the proposal comes from someone who isn&#8217;t an official TinkerPop committer, then they must obtain a committer "sponsor", who is responsible for and endorses the content. (This can be asked on the DISCUSS thread).</p> </li> <li> <p>The proposal must achieve lazy consensus at minimum.</p> </li> <li> <p>After obtaining approval from the DISCUSS thread, start planning and promoting your stream/video!</p> </li> </ol> </div> <div class="paragraph"> <p>The process above applies to two content models: a class of content and individual content. A class of content refers to a content series which would encompass several individual streams or recordings, whereas individual content would be a single one-off stream or recording (which presumably does not fit into an established class). Proposing either a class of content or individual content both require a DISCUSS thread as written above, but once a class is established, content makers are free to make content under the model defined by that class without having a special DISCUSS thread for each recording or stream. You may, of course, use the dev list to organize a particular stream or recording for a particular content class.</p> </div> <div class="paragraph"> <p>The following bullets represent current content classes:</p> </div> <div class="ulist"> <ul> <li> <p>A <em>Contributorcast</em> is like a podcast and is held by contributors to the TinkerPop project. They may or may not be official committers, but they should have submitted merged pull requests of significance or have some recognized standing in Stackoverflow, Discord or just generally the TinkerPop Community. At least one official committer must be on the stream and those committers are in charge of content presented and those other contributors they invite to join. There is no particular format for this content and it is meant to be informal in its style but informational and more often than not about TinkerPop itself.</p> </li> <li> <p><em>TinkerPop Wide</em> streams are about the wider TinkerPop Community. They focus on third-party tools, libraries, graph system implementations and applications that are powered by TinkerPop, but might also stretch into the general graph world. An official committer must host the stream.</p> </li> </ul> </div> <div class="paragraph"> <p>All media needs to achieve a certain level of consistency and quality with existing content. Content creators and their guests should abide by the community guidelines for <a href="https://safety.twitch.tv/s/article/Community-Guidelines?language=en_US">Twitch</a>, <a href="https://www.youtube.com/howyoutubeworks/policies/community-guidelines/">YouTube</a> and the <a href="https://community.apache.org/contributors/etiquette">ASF</a> when producing media.</p> </div> </div> </div> <div class="sect1"> <h2 id="_release_notes">Release Notes</h2> <div class="sectionbody"> <div class="paragraph"> <p>There is a two-pronged approach to maintaining the change log and preparing the release notes.</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p>For work that is documented in JIRA, run the release notes report to include all of the tickets targeted for a specific release. This report can be included in the release announcement.</p> </li> <li> <p>The manual change log (<code>CHANGELOG.asciidoc</code>) can be used to highlight large changes, describe themes (e.g. "We focused on performance improvements") or to give voice to undocumented changes.</p> </li> </ol> </div> <div class="paragraph"> <p>Given the dependence on the JIRA report for generating additions to the <code>CHANGELOG.asciidoc</code>, which uses the title of the issue as the line presented in the release note report, titles should be edited prior to release to be useful in that context. In other words, an issue title should be understandable as a change in the fewest words possible while still conveying the gist of the change.</p> </div> <div class="paragraph"> <p>Changes that break the public APIs should be marked with a "breaking" label and should be distinguished from other changes in the release notes.</p> </div> </div> </div> <div class="sect1"> <h2 id="branches">Branches</h2> <div class="sectionbody"> <div class="paragraph"> <p>TinkerPop has several release branches:</p> </div> <div class="ulist"> <ul> <li> <p><code>3.0-dev</code> - 3.0.x (no longer maintained)</p> </li> <li> <p><code>3.1-dev</code> - 3.1.x (no longer maintained)</p> </li> <li> <p><code>3.2-dev</code> - 3.2.x (no longer maintained)</p> </li> <li> <p><code>3.3-dev</code> - 3.3.x (no longer maintained)</p> </li> <li> <p><code>3.4-dev</code> - 3.4.x (no longer maintained)</p> </li> <li> <p><code>3.5-dev</code> - 3.5.x (no longer maintained)</p> </li> <li> <p><code>3.6-dev</code> - 3.6.x (non-breaking bug fixes and enhancements)</p> </li> <li> <p><code>3.7-dev</code> - 3.7.x (non-breaking bug fixes and enhancements)</p> </li> <li> <p><code>master</code> - 4.x (current development)</p> </li> </ul> </div> <div class="paragraph"> <p>The branch description above that reads "non-breaking bug fixes and enhancements" simply means that within that release line (i.e. patch version) changes should not alter existing behavior, introduce new APIs, change serialization formats, modify protocols, etc. In this way, users and providers have an easy way to know that within a minor release line, they can be assured that their upgrades will not introduce potential problems. A good rule of thumb is to consider whether a client of one version within a release line can interact properly with a server version within that same line. If so, it is likely an acceptable change within that branch.</p> </div> <div class="paragraph"> <p>Changes to earlier branches should merge forward toward <code>master</code> (e.g. <code>3.6-dev</code> should merge to <code>master</code>). Please read more about this process in the <a href="#pull-requests">Pull Requests</a> section.</p> </div> <div class="paragraph"> <p>As described in <a href="#versioning">versioning</a>, it is possible to do a "partial" release which will utilize a four-digit version that starts with a "1" (e.g. <code>3.6.0.1</code>). The branching strategy for a partial release requires that a <code>-dev</code> branch be created with the three digit prefix.</p> </div> <div class="paragraph"> <p>Other branches may be created for collaborating on features or for RFC&#8217;s that other developers may want to inspect. It is suggested that the JIRA issue ID be used as the prefix, since that triggers certain automation, and it provides a way to account for the branch lifecycle, i.e. "Who&#8217;s branch is this, and can I delete it?"</p> </div> <div class="paragraph"> <p>For branches that are NOT associated with JIRA issues, developers should utilize their Apache ID as a branch name prefix. This provides a unique namespace, and also a way to account for the branch lifecycle.</p> </div> <div class="paragraph"> <p>Developers should remove their own branches when they are no longer needed.</p> </div> </div> </div> <div class="sect1"> <h2 id="_tags">Tags</h2> <div class="sectionbody"> <div class="paragraph"> <p>Tags are used for milestones, release candidates, and approved <a href="#versioning">partial and complete releases</a>. Tags for a complete release are simply defined by the three-digit version number. Tags for a partial release, should be prefixed by the individual language relevant to that release. For example, if there is an initial partial release for <code>3.6.0</code> on <code>gremlin-python</code> then the tag should be <code>3.6.0.1-python</code>.</p> </div> <div class="paragraph"> <p>Please refrain from creating arbitrary tags, as they produce permanent clutter.</p> </div> </div> </div> <div class="sect1"> <h2 id="runtimes">Runtimes</h2> <div class="sectionbody"> <div class="paragraph"> <p>Each programming language has a runtime that TinkerPop supports. In general, TinkerPop will attempt to support the current LTS version for a particular major version for the lifetime of its minor and patch releases. A deprecation notice will be applied to the last release of TinkerPop that will support an outdated runtime and the runtime will be updated in the following release. Part of each major release cycle, should include some analysis of the current runtime supported.</p> </div> <div class="ulist"> <ul> <li> <p>Java - Typically, TinkerPop will find itself bound to the version held by its major dependencies like Apache Spark.</p> </li> <li> <p>Javascript - Consult <a href="https://github.com/nodejs/Release">nodejs/Release</a> for the current active LTS of node and <a href="https://nodejs.org/en/download/releases/">here</a> for npm compatibility.</p> </li> <li> <p>.NET - Consult <a href="https://dotnet.microsoft.com/platform/support/policy/dotnet-core">.NET Release Lifecycle</a> for LTS status.</p> </li> <li> <p>Python - Consult <a href="https://www.python.org/downloads/">Python.org</a> for the current LTS status.</p> </li> </ul> </div> </div> </div> <div class="sect1"> <h2 id="_issue_tracker_conventions">Issue Tracker Conventions</h2> <div class="sectionbody"> <div class="paragraph"> <p>TinkerPop uses Apache JIRA as its <a href="https://issues.apache.org/jira/browse/TINKERPOP">issue tracker</a>. JIRA is a very robust piece of software with many options and configurations. To simplify usage and ensure consistency across issues, the following conventions should be adhered to:</p> </div> <div class="ulist"> <ul> <li> <p>An issue&#8217;s "status" should generally be in one of two states: <code>open</code> or <code>closed</code> (<code>reopened</code> is equivalent to <code>open</code> for our purposes).</p> <div class="ulist"> <ul> <li> <p>An <code>open</code> issue is newly created, under consideration or otherwise in progress.</p> </li> <li> <p>A <code>closed</code> issue is completed for purposes of release (i.e. code, testing, and documentation complete).</p> </li> <li> <p>Issues in a <code>resolved</code> state should immediately be evaluated for movement to <code>closed</code> - issue become <code>resolved</code> by those who don&#8217;t have the permissions to <code>close</code>.</p> </li> </ul> </div> </li> <li> <p>An issue&#8217;s "type" should be one of two options: <code>bug</code> or <code>improvement</code>.</p> <div class="ulist"> <ul> <li> <p>A <code>bug</code> has a very specific meaning, referring to an error that prevents usage of TinkerPop AND does not have a reasonable workaround. Given that definition, a <code>bug</code> should generally have very high priority for a fix.</p> </li> <li> <p>Everything else is an <code>improvement</code> in the sense that any other work is an enhancement to the current codebase.</p> </li> </ul> </div> </li> <li> <p>The "component" should be representative of the primary area of code that it applies to and all issues should have this property set.</p> </li> <li> <p>Issues are not assigned "labels" with two exceptions:</p> <div class="ulist"> <ul> <li> <p>The "breaking" label which marks an issue as one that is representative of a change in the API that might affect users or providers. This label is important when organizing release notes.</p> </li> <li> <p>The "deprecation" label which is assigned to an issue that includes changes to deprecate a portion of the API.</p> </li> </ul> </div> </li> <li> <p>The "affects/fix version(s)" fields should be appropriately set, where the "fix version" implies the version on which that particular issue will completed. This is a field usually only set by committers and should only be set when the issue is being closed with a completed disposition (e.g. "Done", "Fixed", etc.).</p> </li> <li> <p>The "priority" field can be arbitrarily applied with one exception. The "trivial" option should be reserved for tasks that are "easy" for a potential new contributor to jump into and do not have significant impact to urgently required improvements.</p> </li> <li> <p>The "resolution" field which is set on the close of the issue should specify the status most closely related to why the issue was closed. In most cases, this will mean "Fixed" for a "Bug" or "Done" for an "Improvement". Only one resolution has special meaning and care should be taken with this particular option: "Later". "Later" means that the item is a good idea but likely will not be implemented in any foreseeable future. By closing uncompleted issues with this resolution, it should be easy to come back to them later when needed.</p> </li> </ul> </div> </div> </div> <div class="sect1"> <h2 id="_code_style">Code Style</h2> <div class="sectionbody"> <div class="paragraph"> <p>Contributors should examine the current code base to determine what the code style patterns are and should match their style to what is already present. Of specific note however, TinkerPop does not use "import wildcards" - IDEs should be adjusted accordingly to not auto-wildcard the imports.</p> </div> </div> </div> <div class="sect1"> <h2 id="_build_server">Build Server</h2> <div class="sectionbody"> <div class="paragraph"> <p>TinkerPop uses <a href="https://docs.github.com/en/actions">GitHub Actions</a> for <a href="https://en.wikipedia.org/wiki/Continuous_integration">CI</a> services. The build status can be found <a href="https://github.com/apache/tinkerpop/actions">here</a>. There is a single "build-test" workflow that runs a number of jobs that break the test execution into a series of smaller test executions. Taken together, they provide a solid cross section of coverage of the code base</p> </div> </div> </div> <div class="sect1"> <h2 id="_deprecation">Deprecation</h2> <div class="sectionbody"> <div class="paragraph"> <p>When possible, committers should avoid direct "breaking" change (e.g. removing a method from a class) and favor deprecation. Deprecation should come with sufficient documentation and notice especially when the change involves public APIs that might be utilized by users or implemented by providers:</p> </div> <div class="ulist"> <ul> <li> <p>Mark the code with the <code>@Deprecated</code> annotation.</p> </li> <li> <p>Use javadoc to further document the change with the following content:</p> <div class="ulist"> <ul> <li> <p><code>@deprecated As of release 3.7.3, replaced by {@link SomeOtherClass#someNewMethod()}</code> - if the method is not replaced then the comment can simply read "not replaced". Additional comments that provide more context are encouraged.</p> </li> <li> <p><code>@see &lt;a href="https://issues.apache.org/jira/browse/TINKERPOP-XXX"&gt;TINKERPOP-XXX&lt;/a&gt;</code> - supply a link to the JIRA issue for reference - the issue should include the "deprecation" label.</p> </li> </ul> </div> </li> <li> <p>Be sure that deprecated methods are still under test - consider using javadoc/comments in the tests themselves to call out this fact.</p> </li> <li> <p>Create a new JIRA issue to track removal of the deprecation for future evaluation.</p> </li> <li> <p>Update the "upgrade documentation" to reflect the API change and how the reader should resolve it.</p> </li> </ul> </div> <div class="paragraph"> <p>The JIRA issues that track removal of deprecated methods should be periodically evaluated to determine if it is prudent to schedule them into a release.</p> </div> </div> </div> <div class="sect1"> <h2 id="developing-tests">Developing Tests</h2> <div class="sectionbody"> <div class="paragraph"> <p>TinkerPop has a wide variety of test types that help validate its internal code as well as external provider code. There are "unit tests" and "integration tests". Unit tests execute on standard runs of <code>mvn clean install</code>. These tests tend to run quickly and provide a reasonable level of coverage and confidence in the code base. Integration tests are disabled by default and must be explicitly turned on with a special build property by adding <code>-DskipIntegrationTests=false</code> to the <code>mvn</code> execution. Integration tests run slower and may require external components to be running when they are executed. They are "marked" as separate from unit tests by inclusion of the suffix "IntegrateTest".</p> </div> <div class="paragraph"> <p>Here are some other points to consider when developing tests:</p> </div> <div class="ulist"> <ul> <li> <p>Avoid use of <code>println</code> in tests and prefer use of a SLF4j <code>Logger</code> instance so that outputs can be controlled in a standard way.</p> </li> <li> <p>If it is necessary to create files on the filesystem, do not hardcode directories - instead, use the <code>TestHelper</code> to create directory structures. <code>TestHelper</code> will properly create file system structure in the appropriate build directory thus allowing proper clean-up between test runs.</p> </li> <li> <p>If writing tests in one of the test suites, like <code>gremlin-test</code>, it is important to remember that if a new <code>Graph</code> instance is constructed within the test manually, that it be closed on exit of that test. Failing to do this cleanup can cause problems for some graph providers.</p> </li> <li> <p>Tests that are designed to use a <code>GraphProvider</code> implementation in conjunction with <code>AbstractGremlinTest</code> <em>and</em> are in the <code>/test</code> directory should not be named with <code>Test</code> as the suffix, as this will cause them to execute in some environments without a <code>GraphProvider</code> being initialized by a suite. These types of tests should be suffixed with <code>Check</code> instead. Please see <a href="https://github.com/apache/tinkerpop/blob/e32a4187e4f25e290aabe14007f9087c48a06521/neo4j-gremlin/src/test/java/org/apache/tinkerpop/gremlin/neo4j/structure/NativeNeo4jStructureCheck.java">NativeNeo4jStructureCheck</a> for an example.</p> </li> </ul> </div> <div class="sect2"> <h3 id="gremlin-language-test-cases">Gremlin Language Test Cases</h3> <div class="paragraph"> <p>Test cases for the Gremlin Language currently requires that the newly developed test be added in two places:</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p>As a test written in Java in the <code>gremlin-test</code> module within the subpackages of <code>org.apache.tinkerpop.gremlin.process.traversal.step</code></p> </li> <li> <p>As a test written in Gherkin in the <code>gremlin-test</code> module in the <code>/features</code> subdirectory</p> </li> </ol> </div> <div class="paragraph"> <p>When writing a Java test case for a Gremlin step, be sure to use the following conventions.</p> </div> <div class="ulist"> <ul> <li> <p>The name of the traversal generator should start with <code>get</code>, use <code>X</code> for brackets, <code>_</code> for space, and the Gremlin-Groovy sugar syntax.</p> <div class="ulist"> <ul> <li> <p><code>get_g_V_hasLabelXpersonX_groupXaX_byXageX_byXsumX_name()</code></p> </li> </ul> </div> </li> <li> <p>When creating a test for a step that has both a barrier and sideEffect form (e.g. <code>group()</code>, <code>groupCount()</code>, etc.), test both representations.</p> <div class="ulist"> <ul> <li> <p><code>get_g_V_groupCount_byXnameX()</code></p> </li> <li> <p><code>get_g_V_groupCountXaX_byXnameX_capXaX()</code></p> </li> </ul> </div> </li> <li> <p>The name of the actual test case should be the name of the traversal generator minus the <code>get_</code> prefix.</p> </li> <li> <p>The Gremlin-Groovy version of the test should use the sugar syntax in order to test sugar (as Gremlin-Java tests test standard syntax).</p> <div class="ulist"> <ul> <li> <p><code>g.V.age.sum</code></p> </li> </ul> </div> </li> <li> <p>Avoid using lambdas in the test case unless that is explicitly what is being tested as OLAP systems will typically not be able to execute those tests.</p> </li> <li> <p><code>AbstractGremlinProcessTest</code> has various static methods to make writing a test case easy.</p> <div class="ulist"> <ul> <li> <p><code>checkResults(Arrays.asList("marko","josh"), traversal)</code></p> </li> <li> <p><code>checkMap(new HashMap&lt;String,Long&gt;() {{ put("marko",1l); }}, traversal.next())</code></p> </li> </ul> </div> </li> </ul> </div> <div class="paragraph"> <p>Gherkin tests follow some important conventions and have a sub-language that must be adhered to for the tests to function properly. Note that Gherkin tests are designed to support the testing of GLVs and at some point will likely replace the Java tests. If a new Java test is added and an associated Gherkin tests is not, the overall build will fail the <code>FeatureCoverageTest</code> of <code>gremlin-test</code> which validates that all tests written in Java are also implemented in Gherkin.</p> </div> <div class="paragraph"> <p>The basic syntax of a Gherkin test is as follows:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="gherkin">Scenario: g_VX1X_unionXrepeatXoutX_timesX2X__outX_name Given the modern graph And using the parameter vId1 defined as &quot;v[marko].id&quot; And the traversal of &quot;&quot;&quot; g.V(vId1).union(__.repeat(__.out()).times(2), __.out()).values(&quot;name&quot;) &quot;&quot;&quot; When iterated to list Then the result should be unordered | result | | ripple | | lop | | lop | | vadas | | josh |</code></pre> </div> </div> <div class="sect3"> <h4 id="_scenario_name">Scenario Name</h4> <div class="paragraph"> <p>The name of the scenario needs to match the name of the Java test. If it does not then the <code>FeatureCoverageTest</code> will fail.</p> </div> </div> <div class="sect3"> <h4 id="_given">Given</h4> <div class="paragraph"> <p>"Given" sets the context of the test. Specifically, it establishes the graph that will be used for the test. It conforms to the pattern of "Given the <em>xxx</em> graph" where the "xxx" may be one of the following:</p> </div> <div class="ulist"> <ul> <li> <p>empty</p> </li> <li> <p>modern</p> </li> <li> <p>classic</p> </li> <li> <p>crew</p> </li> <li> <p>sink</p> </li> <li> <p>grateful</p> </li> </ul> </div> <div class="paragraph"> <p>Never modify the data of any of the graphs except for the "empty" graph. The "empty" graph is the only graph that is guaranteed to be refreshed between tests. The "empty" graph maybe be modified by the traversal under test or by an additional "Given" option:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="gherkin">Given the empty graph And the graph initializer of &quot;&quot;&quot; g.addV(&quot;person&quot;).property(T.id, 1).property(&quot;name&quot;, &quot;marko&quot;).property(&quot;age&quot;, 29).as(&quot;marko&quot;). addV(&quot;person&quot;).property(T.id, 2).property(&quot;name&quot;, &quot;vadas&quot;).property(&quot;age&quot;, 27).as(&quot;vadas&quot;). addV(&quot;software&quot;).property(T.id, 3).property(&quot;name&quot;, &quot;lop&quot;).property(&quot;lang&quot;, &quot;java&quot;).as(&quot;lop&quot;). addV(&quot;person&quot;).property(T.id, 4).property(&quot;name&quot;,&quot;josh&quot;).property(&quot;age&quot;, 32).as(&quot;josh&quot;). addV(&quot;software&quot;).property(T.id, 5).property(&quot;name&quot;, &quot;ripple&quot;).property(&quot;lang&quot;, &quot;java&quot;).as(&quot;ripple&quot;). addV(&quot;person&quot;).property(T.id, 6).property(&quot;name&quot;, &quot;peter&quot;).property(&quot;age&quot;, 35).as('peter'). addE(&quot;knows&quot;).from(&quot;marko&quot;).to(&quot;vadas&quot;).property(T.id, 7).property(&quot;weight&quot;, 0.5). addE(&quot;knows&quot;).from(&quot;marko&quot;).to(&quot;josh&quot;).property(T.id, 8).property(&quot;weight&quot;, 1.0). addE(&quot;created&quot;).from(&quot;marko&quot;).to(&quot;lop&quot;).property(T.id, 9).property(&quot;weight&quot;, 0.4). addE(&quot;created&quot;).from(&quot;josh&quot;).to(&quot;ripple&quot;).property(T.id, 10).property(&quot;weight&quot;, 1.0). addE(&quot;created&quot;).from(&quot;josh&quot;).to(&quot;lop&quot;).property(T.id, 11).property(&quot;weight&quot;, 0.4). addE(&quot;created&quot;).from(&quot;peter&quot;).to(&quot;lop&quot;).property(T.id, 12).property(&quot;weight&quot;, 0.2) &quot;&quot;&quot;</code></pre> </div> </div> <div class="paragraph"> <p>The above configuration will use the "empty" graph and initialize it with the specified traversal. In this case, that traversal loads the "empty" graph with the "modern" graph.</p> </div> <div class="paragraph"> <p>Once the graph for the test is defined, the context can be expanded to include parameters that will be applied to the traversal under test. Any variable value being used in the traversal under test, especially ones that require a specific type, should be defined as parameters. The structure for parameter definition looks like this:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="gherkin">Given the modern graph And using the parameter vId1 defined as &quot;v[marko].id&quot;</code></pre> </div> </div> <div class="paragraph"> <p>In the above example, "vId1" is the name of the parameter that will be used in the traversal. The end of that line in quotes is the value of that parameter and should use the type system notation that has been developed for the TinkerPop Gherkin tests. The type system notation ensures that different language variants have the ability to construct the appropriate types expected by the tests.</p> </div> <div class="paragraph"> <p>The syntax of the type notation involves a prefix character to help denote the type, a value between two square brackets, optionally suffixed with some additional notation depending on the primary type.</p> </div> <div class="ulist"> <ul> <li> <p>Date - <strong>dt[<em>xxx</em>]</strong> - The "xxx" should be ISO 8601 string.</p> </li> <li> <p>Edge - <strong>e[<em>xxx</em>]</strong> - The "xxx" should be replaced with a representation of an edge in the form of the <code>vertex_name-edgelabel&#8594;vertex_name</code>. This syntax may also include the <code>.id</code> suffix which would indicate getting the edge identifier or the <code>.sid</code> suffix which gets a string representation of the edge identifier.</p> </li> <li> <p>Lambda - <strong>c[<em>xxx</em>]</strong> - The "xxx" should contain a lambda written in Groovy.</p> </li> <li> <p>List - <strong>l[<em>xxx</em>,<em>yyy</em>,<em>zzz</em>,&#8230;&#8203;]</strong> - A comma separated collection of values that make up the list should be added to between the square brackets. These values respect the type system thus allowing for creation of lists of vertices, edges, maps, and any other available type. Spaces are taken literally, therefore it is important to avoid spaces unless they are required for the test.</p> </li> <li> <p>Map - <strong>m[<em>xxx</em>]</strong> - The "xxx" should be replaced with a JSON string. Note that keys and values will be parsed using the type notation system so that it is possible to have maps containing arbitrary keys and values.</p> </li> <li> <p>Numeric - <strong>d[<em>xxx</em>].<em>y</em></strong> - The "xxx" should be replaced with a number. The suffix denoted by "y" should always be included to further qualify the type of numeric. The following options are available:</p> <div class="ulist"> <ul> <li> <p><strong>b</strong> - 8-bit byte</p> </li> <li> <p><strong>s</strong> - 16-bit Short</p> </li> <li> <p><strong>i</strong> - 32-bit Integer</p> </li> <li> <p><strong>f</strong> - 32-bit Float</p> </li> <li> <p><strong>l</strong> - 64-bit Long</p> </li> <li> <p><strong>d</strong> - 64-bit Double</p> </li> <li> <p><strong>m</strong> - Arbitrary-precision signed decimal numbers (e.g. <code>BigDecimal</code> in Java)</p> </li> <li> <p><strong>n</strong> - Arbitrary-precision integers (e.g. <code>BigInteger</code> in Java)</p> </li> </ul> </div> </li> <li> <p>Path - <strong>p[<em>xxx</em>,<em>yyy</em>,<em>zzz</em>,&#8230;&#8203;]</strong> - A comma separated collection of values that make up the <code>Path</code> should be added to between the square brackets. These values respect the type system thus allowing for creation of <code>Path</code> of vertices, edges, maps, and any other available type.</p> </li> <li> <p>Set - <strong>s[<em>xxx</em>,<em>yyy</em>,<em>zzz</em>,&#8230;&#8203;]</strong> - A comma separated collection of values that make up the set should be added to between the square brackets. These values respect the type system thus allowing for creation of sets of vertices, edges, maps, and any other available type.</p> </li> <li> <p>String - Any value not using the system notation will be interpreted as a string by default.</p> <div class="ulist"> <ul> <li> <p><strong>str[<em>xxx</em>]</strong> (Optional) - xxx should be replaced with a string. Optional notation used for specific string results, such as null and spaces.</p> </li> </ul> </div> </li> <li> <p>T - <strong>t[<em>xxx</em>]</strong> - The "xxx" should be replaced with a value of the <code>T</code> enum, such as <code>id</code> or <code>label</code>.</p> </li> <li> <p>Vertex - <strong>v[<em>xxx</em>]</strong> - The "xxx" should be replaced with the "name" property of a vertex in the graph. This syntax may include the <code>.id</code> suffix which would indicate getting the vertex identifier or the <code>.sid</code> suffix which gets a string representation of the edge identifier.</p> </li> </ul> </div> <div class="paragraph"> <p>In addition, parameter names should adhere to a common form as they hold some meaning to certain language variant implementations:</p> </div> <div class="ulist"> <ul> <li> <p>General variables of no particular type should use <code>xx1</code>, <code>xx2</code> and <code>xx3</code>.</p> </li> <li> <p>A <code>Vertex</code> variable should be prefixed with "v" and be followed by the <code>id</code>, therefore, <code>v1</code> would signify a <code>Vertex</code> with the <code>id</code> of "1".</p> </li> <li> <p>An <code>Edge</code> variable follows the pattern of vertices but with a "e" prefix.</p> </li> <li> <p>The "id" of a <code>Vertex</code> or <code>Edge</code> is prefixed with "vid"`" or "eid" respectively followed by the <code>id</code>, thus, <code>vid1</code> would be "1" and refer to the <code>Vertex</code> with that <code>id</code>.</p> </li> <li> <p><code>Function</code> variables should use <code>l1</code> and <code>l2</code>.</p> </li> <li> <p><code>Predicate</code> variables should use <code>pred1</code>.</p> </li> <li> <p><code>Comparator</code> variables should use <code>c1</code> and <code>c2</code>.</p> </li> </ul> </div> <div class="paragraph"> <p>Finally, specify the traversal under test with the "Given" option "and the traversal":</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="gherkin">And the traversal of &quot;&quot;&quot; g.V(vId1).union(__.repeat(__.out()).times(2), __.out()).values(&quot;name&quot;) &quot;&quot;&quot;</code></pre> </div> </div> <div class="paragraph"> <p>The traversal must be written so that it can be parsed by both <code>gremlin-groovy</code> and <code>gremlin-language</code>. Using syntax particular to one but not the other may result in test execution errors.</p> </div> <div class="paragraph"> <p>It will be the results of this traversal that end up being asserted by Gherkin. When writing these test traversals, be sure to always use the method and enum prefixes. For example, use <code>__.out()</code> for an anonymous traversal rather than just <code>out()</code> and prefer <code>Scope.local</code> rather than just <code>local</code>.</p> </div> <div class="paragraph"> <p>If a particular test cannot be written in Gherkin for some reason or cannot be otherwise supported by a GLV, first, consider whether or not this test can be re-written in Java so that it will work for GLVs and then, second, if it cannot, then use the following syntax for unsupported tests:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="gherkin">Scenario: g_V_outXcreatedX_groupCountXxX_capXxX Given an unsupported test Then nothing should happen because &quot;&quot;&quot; The result returned is not supported under GraphSON 2.x and therefore cannot be properly asserted. More specifically it has vertex keys which basically get toString()'d under GraphSON 2.x. This test can be supported with GraphSON 3.x. &quot;&quot;&quot;</code></pre> </div> </div> </div> <div class="sect3"> <h4 id="_when">When</h4> <div class="paragraph"> <p>The "When" options get the result from the traversal in preparation for assertion. There are two options to iterate:</p> </div> <div class="ulist"> <ul> <li> <p>"When iterated to list" - iterates the entire traversal into a list result that is asserted</p> </li> <li> <p>"When iterated next" - gets the first value from the traversal as the result to be asserted</p> </li> </ul> </div> <div class="paragraph"> <p>There should be only one "When" defined in a scenario.</p> </div> </div> <div class="sect3"> <h4 id="_then">Then</h4> <div class="paragraph"> <p>The "Then" options handle the assertion of the result. There are several options to consider:</p> </div> <div class="ulist"> <ul> <li> <p>"the result should have a count of <em>xxx</em>" - assumes a list value in the result and counts the number of values in it</p> </li> <li> <p>"the result should be empty" - no results</p> </li> <li> <p>"the traversal will raise an error" - an exception is thrown as a result of traversal iteration</p> </li> <li> <p>"the traversal will raise an error with message <em>comparison</em> text of <em>message</em>" - an exception is thrown as a result of traversal iteration where "<em>comparison</em>" may be one of "containing", "starting", or "ending".</p> </li> <li> <p>"the result should be ordered" - the exact results and should appear in the order presented</p> </li> <li> <p>"the result should be unordered" - the exact results but can appear any order</p> </li> <li> <p>"the result should be of" - results can be any of the specified values and in any order (use when guarantees regarding the exact results cannot be pre-determined easily - see the <code>range()</code>-step tests for examples)</p> </li> </ul> </div> <div class="paragraph"> <p>These final three types of assertions mentioned above should be followed by a Gherkin table that has one column, where each row value in that column represents a value to assert in the result. These values are type notation respected as shown in the following example:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="gherkin">Then the result should be unordered | result | | ripple | | lop | | lop | | vadas | | josh |</code></pre> </div> </div> <div class="paragraph"> <p>Another method of assertion is to test mutations in the original graph. Again, mutations should only occur on the "empty" graph, but they can be validated as follows:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="gherkin">Scenario: g_V_outE_drop Given the empty graph And the graph initializer of &quot;&quot;&quot; g.addV().as(&quot;a&quot;).addV().as(&quot;b&quot;).addE(&quot;knows&quot;).to(&quot;a&quot;) &quot;&quot;&quot; And the traversal of &quot;&quot;&quot; g.V().outE().drop() &quot;&quot;&quot; When iterated to list Then the result should be empty And the graph should return 2 for count of &quot;g.V()&quot; And the graph should return 0 for count of &quot;g.E()&quot;</code></pre> </div> </div> </div> <div class="sect3"> <h4 id="gherkin-tags">Tags</h4> <div class="paragraph"> <p>Features have tags associated with them to help allow developers to better break up test runs if they desire. There are two types of tags:</p> </div> <div class="ulist"> <ul> <li> <p><code>@StepClass*</code> - Marks the step grouping and is a prefix that precedes and either refers to one of the following:</p> <div class="ulist"> <ul> <li> <p>One of the four types of steps: <code>Branch</code>, <code>Filter</code>, <code>Map</code>, and <code>SideEffect</code> (e.g. <code>@StepClassBranch</code>)</p> </li> <li> <p><code>Semantics</code> which maps to elements of the <a href="https://tinkerpop.apache.org/docs/3.7.3/dev/provider/#gremlin-semantics">Gremlin Semantics</a> specification.</p> </li> <li> <p>An <code>Integrated</code> grouping that does not fit those individual classifications well.</p> </li> </ul> </div> </li> <li> <p><code>@Step*</code> - Marks testing for a particular step. While this tag is generally unique to the feature file itself and test filtering could be accomplished at that level by way of the file, the use of the tag is a secondary option and allows filtering to be consistently managed by tags alone. The suffix is the Gremlin step itself (e.g. <code>@StepHas</code>) in all cases except for <code>@StepVertex</code> which covers <code>V()</code>, <code>E()</code>, <code>out()</code>, <code>in()</code>, <code>both()</code>, <code>inE()</code>, <code>outE()</code>, and <code>bothE()</code>.</p> </li> </ul> </div> <div class="paragraph"> <p>Scenarios have tags associated with them that help identify subsets of tests so that a test runner can selectively include or ignore certain tests. The tags enable the practical and necessary ability for providers to ignore tests that they simply cannot support. It is important to be aware of the following tags when writing tests as not including a tag when one is necessary will cause provider tests to fail:</p> </div> <div class="ulist"> <ul> <li> <p><code>@AllowNullPropertyValues</code> - The scenario requires that the graph be configured with <code>AllowNullPropertyValues</code> as <code>true</code> (meaning that it can store <code>null</code> values).</p> </li> <li> <p><code>@GraphComputerVerificationInjectionNotSupported</code> - The scenario will not work on with <code>GraphComputer</code> because the <code>inject()</code> step is not supported.</p> </li> <li> <p><code>@GraphComputerVerificationMidVNotSupported</code> - The scenario will not work on with <code>GraphComputer</code> because the mid-<code>V()</code> step is not supported.</p> </li> <li> <p><code>@GraphComputerVerificationOneBulk</code> - The scenario will not work because <code>withBulk(false)</code> is configured and that is not compatible with <code>GraphComputer</code></p> </li> <li> <p><code>@GraphComputerVerificationReferenceOnly</code> - The scenario itself is not written to support <code>GraphComputer</code> because it tries to reference inaccessible properties that are on elements only available by "reference" (i.e <code>T.id</code> only).</p> </li> <li> <p><code>@GraphComputerVerificationStrategyNotSupported</code> - The scenario uses a traversal strategy that is not supported by <code>GraphComputer</code>.</p> </li> <li> <p><code>@GraphComputerVerificationStarGraphExceeded</code> - The scenario itself is not written to support <code>GraphComputer</code> because the traversal does not mind the star graph limitation.</p> </li> <li> <p><code>@InsertionOrderingRequired</code> - The scenario is reliant on the graph system predictably returning results (vertices, edges, properties) in the same order in which they were inserted into the graph.</p> </li> <li> <p><code>@MetaProperties</code> - The scenario makes use of meta-properties.</p> </li> <li> <p><code>@MultiProperties</code> - The scenario makes use of multi-properties.</p> </li> <li> <p><code>@RemoteOnly</code> - The scenario uses some Gremlin syntax that cannot be supported outside of remote test executions. The best example of this sort of test would be one that uses the remote <code>Lambda</code> syntax.</p> </li> <li> <p><code>@UserSuppliedVertexIds</code> - The scenario relies on the vertex IDs specified in the dataset used by the scenario.</p> </li> <li> <p><code>@UserSuppliedEdgeIds</code> - The scenario relies on the edge IDs specified in the dataset used by the scenario.</p> </li> <li> <p><code>@UserSuppliedVertexPropertyIds</code> - The scenario relies on the vertex property IDs specified in the dataset used by the scenario.</p> </li> <li> <p><code>@With*</code> - The scenario uses some <code>with()</code> based configuration like strategies:</p> <div class="ulist"> <ul> <li> <p><code>@WithPartitionStrategy</code></p> </li> <li> <p><code>@WithProductiveByStrategy</code></p> </li> <li> <p><code>@WithReadOnlyStrategy</code></p> </li> <li> <p><code>@WithSeedStrategy</code></p> </li> <li> <p><code>@WithSubgraphStrategy</code></p> </li> </ul> </div> </li> </ul> </div> <div class="paragraph"> <p>Tag filters can be applied to Intellij at execution time by adding a system properties of <code>-Dcucumber.filter.tags=&lt;step-filter&gt;</code>.</p> </div> </div> </div> <div class="sect2"> <h3 id="gremlin-socket-server-tests">Gremlin Socket Server Tests</h3> <div class="paragraph"> <p><code>gremlin-socket-server</code> is an included test server for driver tests which require predefined server behavior. Typically, this is to test scenarios such as the server closing the connection or returning an error code but other response behavior can be added as needed for tests.</p> </div> <div class="paragraph"> <p>Gremlin socket server uses the request id of incoming messages to determine how to respond. Request ids are defined in <code>gremlin-tools/gremlin-socket-server/conf/*.yaml</code>. The server side behavior for each request id is implemented in <code>TestWSGremlinInitializer</code>.</p> </div> <div class="paragraph"> <p>To add new server side behavior, define a new request id in the config yaml, then add a corresponding field in <code>SocketServerSettings</code>. In <code>TestWSGremlinInitializer</code>, add a new block to the if else chain in the decode method which matches the request id. Define all server behavior and responses in this block.</p> </div> <div class="paragraph"> <p>To write the driver test, send a request message to the gremlin socket server port as if it were a normal gremlin server. Override the request id on the request with one defined in the config yaml and gremlin socket server will respond according to the defined behavior.</p> </div> <div class="paragraph"> <p>Ensure that the socket server is running during driver tests. By default, a docker image for <code>gremlin-socket-server</code> is built during <code>mvn install</code>. The simplest way to use the socket server during driver tests is to run a gremlin socket server container during the integration phase of driver tests.</p> </div> </div> </div> </div> <div class="sect1"> <h2 id="_developing_benchmarks">Developing Benchmarks</h2> <div class="sectionbody"> <div class="paragraph"> <p>Benchmarks are a useful tool to track performance between TinkerPop versions and also as tools to aid development decision making. TinkerPop uses <a href="http://openjdk.java.net/projects/code-tools/jmh/">OpenJDK JMH</a> for benchmark development. The JMH framework provides tools for writing robust benchmarking code that avoid many of the pitfalls inherent in benchmarking JIT compiled code on the JVM. Example JMH benchmarks can be found <a href="http://hg.openjdk.java.net/code-tools/jmh/file/tip/jmh-samples/src/main/java/org/openjdk/jmh/samples/">here</a>.</p> </div> <div class="paragraph"> <p>TinkerPop benchmarks live in the <code>gremlin-benchmark</code> module and can either be run from within your IDE or as a standalone uber-jar. The uber-jar is the JMH recommended approach and also makes it easy to distribute artifacts to various environments to gather benchmarking numbers. Having said that, in most cases it should be sufficient to run it from within the IDE.</p> </div> <div class="paragraph"> <p>Benchmarks will not run by default because they are time consuming. To enable benchmarks during the test phase do <code>-DskipBenchmarks=false</code>. To change the number of warmup iterations, measurement iterations, and forks you can do <code>mvn clean test -DskipBenchmarks=false -DdefaultForks=5 -DmeasureIterations=20 -DwarmupIterations=20</code>. Benchmark results will be output by default to the <code>benchmarks</code> directory in JSON format.</p> </div> <div class="paragraph"> <p>Benchmarks may also be run from the command line using the JMH runner. Build the uber-jar and simply run <code>java -jar gremlin-benchmark-TP-VERSION.jar</code>. To see a list of JMH runner options, add the <code>-h</code> flag.</p> </div> <div class="paragraph"> <p>The JUnit/JMH integration was inspired by the Netty projects microbenchmarking suite. Please refer to the Netty <a href="http://netty.io/wiki/microbenchmarks.html">docs</a> for more details. Presently there are 3 abstract benchmark classes that may be used as building blocks for your benchmarks; <code>AbstractBenchmarkBase</code>, <code>AbstractGraphBenchmark</code>, and <code>AbstractGraphMutateBenchmark</code>.</p> </div> <div class="ulist"> <ul> <li> <p><code>AbstractBenchmarkBase</code> - extend when your benchmark does not require a graph instance</p> </li> <li> <p><code>AbstractGraphBenchmark</code> - extend when you are benchmarking read operations against a graph</p> </li> <li> <p><code>AbstractGraphMutateBenchmark</code> - extend when you are benchmarking graph mutation operations eg. <code>g.addV()</code>, <code>graph.addVertex()</code></p> </li> </ul> </div> </div> </div> <div class="sect1"> <h2 id="rtc">Review then Commit</h2> <div class="sectionbody"> <div class="paragraph"> <p>Code modifications must go through a <a href="http://www.apache.org/foundation/glossary.html#ReviewThenCommit">review-then-commit</a> (RTC) process before being merged into a release branch. All committers should follow the pattern below, where "you" refers to the committer wanting to put code into a release branch.</p> </div> <div class="ulist"> <ul> <li> <p>Make a JIRA ticket for the software problem you want to solve (i.e. a fix).</p> </li> <li> <p>Fork the release branch that the fix will be put into.</p> <div class="ulist"> <ul> <li> <p>The branch name should be the JIRA issue identifier (e.g. <code>TINKERPOP-XXX</code>).</p> </li> </ul> </div> </li> <li> <p>Develop your fix in your branch.</p> </li> <li> <p>When your fix is complete and ready to merge, issue a <a href="#pull-requests">pull request</a>.</p> <div class="ulist"> <ul> <li> <p>Be certain that the test suite is passing.</p> </li> <li> <p>If you updated documentation, be sure that the <code>process-docs.sh</code> is building the documentation correctly.</p> </li> </ul> </div> </li> <li> <p>Before you can merge your branch into the release branch, you must have at least 3 +1 <a href="http://www.apache.org/foundation/glossary.html#ConsensusApproval">consensus votes</a> from other committers OR a single +1 from a committer and a seven day review period for objections (i.e. a "cool down period") at which point we will assume a lazy consensus.</p> <div class="ulist"> <ul> <li> <p>Please see the Apache Software Foundations regulations regarding <a href="http://www.apache.org/foundation/voting.html#votes-on-code-modification">Voting on Code Modifications</a>.</p> </li> <li> <p>With the "cool down" process and lazy consensus the single +1 may (should) come from the committer who submitted the pull request (in other words, the change submitter and the reviewer are the same person).</p> </li> <li> <p>Committers are trusted with their changes, but are expected to request reviews for complex changes as necessary and not rely strictly on lazy consensus.</p> </li> </ul> </div> </li> <li> <p>Votes are issued by TinkerPop committers as comments to the pull request.</p> </li> <li> <p>Once either consensus position is reached, you are responsible for merging to the release branch and handling any merge conflicts.</p> <div class="ulist"> <ul> <li> <p>If there is a higher version release branch that requires your fix (e.g. <code>3.y-1.z</code> fix going to a <code>3.y.z</code> release), multiple pull requests may be necessary (i.e. one for each branch).</p> </li> </ul> </div> </li> <li> <p>Be conscious of deleting your branch if it is no longer going to be used so stale branches don&#8217;t pollute the repository.</p> </li> </ul> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> These steps also generally apply to external pull requests from those who are not official Apache committers. In this case, the person responsible for the merge after voting is typically the first person available who is knowledgeable in the area that the pull request affects. Any additional coordination on merging can be handled via the pull request comment system. </td> </tr> </table> </div> <div class="paragraph"> <p>For those performing reviews as part of this process it is worth noting that the notion of "review" is fairly wide for our purposes. TinkerPop has grown into a large and complex code base and very few people (if anyone) is knowledgeable on all of its modules. Detailed code reviews might often be difficult or impossible as a result.</p> </div> <div class="paragraph"> <p>To be clear, a "review" need not be specifically about the exact nature of the code. It is perfectly reasonable to review (and VOTE) in the following fashion:</p> </div> <div class="ulist"> <ul> <li> <p>VOTE +1 - ran docker integration tests and everything passes</p> </li> <li> <p>VOTE +1 - reviewed the code in detail - solid pull request</p> </li> <li> <p>VOTE +1 - agree with the principle of this pull request but don&#8217;t fully understand the code</p> </li> <li> <p>VOTE +1 - read through the updated documentation and understand why this is important, nice</p> </li> </ul> </div> <div class="paragraph"> <p>Non-committers are welcome to review and VOTE as well and while their VOTEs are not binding, they will be taken as seriously as non-binding VOTEs on releases. Reviewing and VOTEing on pull requests as a non-committer is a great way to contribute to the TinkerPop community and get a good pulse on the changes that are upcoming to the framework.</p> </div> <div class="paragraph"> <p>The following exceptions to the RTC (review-then-commit) model presented above are itemized below. It is up to the committer to self-regulate as the itemization below is not complete and only hints at the types of commits that do not require a review.</p> </div> <div class="ulist"> <ul> <li> <p>You are responsible for a release and need to manipulate files accordingly for the release.</p> <div class="ulist"> <ul> <li> <p><code>Gremlin.version()</code>, CHANGELOG dates, <code>pom.xml</code> version bumps, etc.</p> </li> </ul> </div> </li> <li> <p>You are doing an minor change and it is obvious that an RTC is not required (would be a pointless burden to the community).</p> <div class="ulist"> <ul> <li> <p>The fix is under the <a href="http://www.apache.org/foundation/glossary.html#CommitThenReview">commit-then-review</a> (CTR) policy and lazy consensus is sufficient, where a single -1 vote requires you to revert your changes.</p> </li> <li> <p>Adding a test case, fixing spelling/grammar mistakes in the documentation, fixing LICENSE/NOTICE/etc. files, fixing a minor issue in an already merged branch.</p> </li> </ul> </div> </li> </ul> </div> <div class="paragraph"> <p>When the committer chooses CTR, it is considered good form to include something in the commit message that explains that CTR was invoked and the reason for doing so. For example, "Invoking CTR as this change encompasses minor adjustments to text formatting." CTR based commits will still require manual merging through all release branches. Merges should occur in reverse order, starting with the latest release version first (e.g. if the fix is going to 3.3.x then the change should be merged in the following order <code>master</code>, <code>3.4-dev</code>, <code>3.3-dev</code>).</p> </div> <div class="sect2"> <h3 id="pull-requests">Pull Requests</h3> <div class="paragraph"> <p>When submitting a pull request to one of the <a href="#branches">release branches</a>, be sure it uses the following style:</p> </div> <div class="ulist"> <ul> <li> <p>The title of the pull request is the JIRA ticket number + "colon" + the title of the JIRA ticket.</p> </li> <li> <p>The first line of the pull request message should contain a link to the JIRA ticket.</p> </li> <li> <p>Discuss what you did to solve the problem articulated in the JIRA ticket.</p> </li> <li> <p>Discuss any "extra" work done that go beyond the assumed requirements of the JIRA ticket.</p> </li> <li> <p>Be sure to explain what you did to prove that the issue is resolved.</p> <div class="ulist"> <ul> <li> <p>Test cases written.</p> </li> <li> <p>Integration tests run (if required for the work accomplished).</p> </li> <li> <p>Documentation building (if required for the work accomplished).</p> </li> <li> <p>Any manual testing (though this should be embodied in a test case).</p> </li> </ul> </div> </li> <li> <p>Notes about what you will do when you merge to the respective release branch (e.g. update CHANGELOG).</p> <div class="ulist"> <ul> <li> <p>These types of "on merge tweaks" are typically done to extremely dynamic files to combat and merge conflicts.</p> </li> </ul> </div> </li> <li> <p>If you are a TinkerPop committer, you can VOTE on your own pull request, so please do so.</p> </li> </ul> </div> <div class="paragraph"> <p>A pull request will typically be made to a target <a href="#branches">branch</a>. Assuming that branch is upstream of other release branches (e.g. a pull request made to for the branch containing 3.3.x must merge to the branch that releases 3.4.x), it is important to be sure that those changes are merged to the downstream release branches. If the merge from one release branch to another is not terribly conflicted, it is likely safe to offer a single pull request and then merge through the release branches after review. If there is conflict or the likelihood of test failures in downstream branches then this process is best handled by multiple pull requests: one to each release branch. Release branches with merged changes should be pushed in reverse order, starting with the latest release version first (e.g. if the fix is going to 3.3.x then the change should be merged in the following order: <code>master</code>, 3.4-dev`, <code>3.3-dev</code>).</p> </div> <div class="paragraph"> <p>As an example, consider a situation where there is a feature branch named "TINKERPOP-1234" that contains a fix for the <code>3.4-dev</code> branch:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="bash">`git checkout -b TINKERPOP-1234 3.4-dev` // do a bunch of stuff to implement TINKERPOP-1234 and commit/push git checkout -b &lt;TINKERPOP-1234-master&gt; master git merge TINKERPOP-1234</code></pre> </div> </div> <div class="paragraph"> <p>At this point, there are two branches, with the same set of commits going to <code>3.4-dev</code> and <code>master</code>. Voting will occur on both pull requests. After a successful vote, it is time to merge. If there are no conflicts, then simply <code>git merge</code> both pull requests to their respective branches. If there are conflicts, then there is some added work to do - time to rebase:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="bash">git checkout TINKERPOP-1234 git rebase origin/3.4-dev</code></pre> </div> </div> <div class="paragraph"> <p>Depending on the conflict, it might be a good idea to re-test before going any further, otherwise:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="bash">git push origin TINKERPOP-1234 --force</code></pre> </div> </div> <div class="paragraph"> <p>Now, <code>git rebase</code> has re-written the commit history, which makes a mess of the other pull request to master. This problem is rectified by essentially re-issuing the PR:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="bash">git checkout TINKERPOP-1234-master git reset --hard origin/master git merge TINKERPOP-1234</code></pre> </div> </div> <div class="paragraph"> <p>Again, depending on the changes, it may make sense to re-test at this point, otherwise:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="bash">git push origin TINKERPOP-1234-master --force</code></pre> </div> </div> <div class="paragraph"> <p>It should now be safe to merge both pull requests to their release branches.</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> Always take a moment to review the commits in a particular pull request. Be sure that they are <strong>all</strong> related to the work that was done and that no extraneous commits are present that cannot be explained. Ensuring a pull request only contains the expected commits is the responsibility of the committer as well as the reviewer. </td> </tr> </table> </div> </div> </div> </div> <div class="sect1"> <h2 id="dependencies">Dependencies</h2> <div class="sectionbody"> <div class="paragraph"> <p>There are many dependencies on other open source libraries in TinkerPop modules. When adding dependencies or altering the version of a dependency, developers must consider the implications that may apply to the TinkerPop LICENSE and NOTICE files. There are two implications to consider:</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p>Does the dependency fit an Apache <em>approved</em> license?</p> </li> <li> <p>Given the addition or modification to a dependency, does it mean any change for TinkerPop LICENSE and NOTICE files?</p> </li> </ol> </div> <div class="paragraph"> <p>Understanding these implications is important for insuring that TinkerPop stays compliant with the Apache 2 license that it releases under.</p> </div> <div class="paragraph"> <p>Regarding the first item, refer to the Apache Legal for a list of <a href="http://www.apache.org/legal/resolved.html">approved licenses</a> that are compatible with the Apache 2 license.</p> </div> <div class="paragraph"> <p>The second item requires a bit more effort to follow. The Apache website offers a <a href="http://www.apache.org/dev/licensing-howto.html">how-to guide</a> on the approach to maintaining appropriate LICENSE and NOTICE files, but this guide is designed to offer some more specific guidance as it pertains to TinkerPop and its distribution.</p> </div> <div class="paragraph"> <p>To get started, TinkerPop has both "source" and "binary" LICENSE/NOTICE files:</p> </div> <div class="ulist"> <ul> <li> <p>Source LICENSE/NOTICE relate to files packaged with the released source code distribution: <a href="https://github.com/apache/tinkerpop/blob/master/LICENSE">LICENSE</a> / <a href="https://github.com/apache/tinkerpop/blob/master/NOTICE">NOTICE</a></p> </li> <li> <p>Binary LICENSE/NOTICE relate to files packaged with the released binary distributions:</p> <div class="ulist"> <ul> <li> <p>Gremlin Console <a href="https://github.com/apache/tinkerpop/blob/master/gremlin-console/src/main/static/LICENSE">LICENSE</a> / <a href="https://github.com/apache/tinkerpop/blob/master/gremlin-console/src/main/static/NOTICE">NOTICE</a></p> </li> <li> <p>Gremlin Server <a href="https://github.com/apache/tinkerpop/blob/master/gremlin-server/src/main/static/LICENSE">LICENSE</a> / <a href="https://github.com/apache/tinkerpop/blob/master/gremlin-server/src/main/static/NOTICE">NOTICE</a></p> </li> </ul> </div> </li> </ul> </div> <div class="sect2"> <h3 id="_source_license_and_notice">Source LICENSE and NOTICE</h3> <div class="paragraph"> <p>As dependencies are not typically added to the source distribution (i.e. the source zip distribution), there is typically no need to edit source LICENSE/NOTICE when editing a TinkerPop <code>pom.xml</code>. These files only need to be edited if the distribution has a file added to it. Such a situation may arise from several scenarios, but it would most likely come from the addition of a source file from another library.</p> </div> <div class="ulist"> <ul> <li> <p>If the file being bundled is Apache licensed, then add an entry to NOTICE.</p> </li> <li> <p>If the file being bundled is under a different approved license, then add an entry to LICENSE and include a copy of that LICENSE in the root <code>/licenses</code> directory of the code repository.</p> </li> </ul> </div> </div> <div class="sect2"> <h3 id="_binary_license_and_notice">Binary LICENSE and NOTICE</h3> <div class="paragraph"> <p>The binary LICENSE/NOTICE is perhaps most impacted by changes to the various <code>pom.xml</code> files. After altering the <code>pom.xml</code> file of any module, build <code>gremlin-driver</code>, Gremlin Console and Gremlin Server and examine the contents of the binary distributions:</p> </div> <div class="ulist"> <ul> <li> <p>target/gremlin-driver-3.7.3-uber.jar</p> </li> <li> <p>target/gremlin-console-3.7.3-uber.jar</p> </li> <li> <p>target/apache-tinkerpop-gremlin-console-3.7.3-distribution.zip</p> </li> <li> <p>target/apache-tinkerpop-gremlin-server-3.7.3-distribution.zip</p> </li> </ul> </div> <div class="paragraph"> <p>Apache licensed software does not need to be included in LICENSE, but if the new dependency is an Apache-approved license (e.g. BSD, MIT) then it should be added in the pattern already defined. A copy of the LICENSE should be added to the <code>&lt;project&gt;/src/main/static/licenses</code> directory of the code repository and the <code>maven-shade-plugin</code> section of the <code>gremlin-console</code> and <code>gremlin-driver</code> <code>pom.xml</code> files should be updated to reference this new license file so that it is included in the uber jar.</p> </div> <div class="paragraph"> <p>To determine if changes are required to the NOTICE, first check if the bundled jar has a NOTICE file in it (typically found in <code>/META-INF</code> directory of the jar).</p> </div> <div class="ulist"> <ul> <li> <p>If the bundled file does not have a NOTICE, then no changes to TinkerPop&#8217;s NOTICE are required.</p> </li> <li> <p>If the NOTICE of the file being bundled is NOT Apache licensed, then there is no change to TinkerPop&#8217;s NOTICE.</p> </li> <li> <p>If the NOTICE of the file being bundled is Apache licensed, then include the copyright notification in TinkerPop&#8217;s NOTICE.</p> </li> <li> <p>If the NOTICE of the file being bundled is Apache licensed AND is an Apache Software Foundation project, then ONLY include the portion of that NOTICE in TinkerPop&#8217;s NOTICE that is unrelated to the Apache boilerplate NOTICE. If there is no such portion that is different than the boilerplate then this NOTICE can be excluded (i.e. don&#8217;t alter TinkerPop&#8217;s NOTICE at all).</p> </li> </ul> </div> <div class="paragraph"> <p>Please refer to the <a href="http://www.apache.org/dev/licensing-howto.html#mod-notice">Modifications to Notice</a> section of the Apache "Licensing How-to" for more information.</p> </div> </div> </div> </div> <div class="sect1"> <h2 id="documentation">Documentation</h2> <div class="sectionbody"> <div class="paragraph"> <p>The documentation for TinkerPop is stored in the git repository in <code>docs/src/</code> and are then split into several subdirectories, each representing a "book" (or its own publishable body of work). If a new AsciiDoc file is added to a book, then it should also be included in the <code>index.asciidoc</code> file for that book, otherwise the preprocessor will ignore it. Likewise, if a whole new book (subdirectory) is added, it must include an <code>index.asciidoc</code> file to be recognized by the AsciiDoc preprocessor.</p> </div> <div class="paragraph"> <p>Adding a book also requires a change to the root <code>pom.xml</code> file. Find the "asciidoc" Maven profile and add a new <code>&lt;execution&gt;</code> to the <code>asciidoctor-maven-plugin</code> configuration. For each book in <code>docs/src/</code>, there should be a related <code>&lt;execution&gt;</code> that generates the HTML from the AsciiDoc. Follows the patterns already established by the existing <code>&lt;execution&gt;</code> entries, paying special attention to the pathing of the '&lt;sourceDirectory&gt;', <code>&lt;outputDirectory&gt;</code> and <code>&lt;imagesdir&gt;</code>. Note that the <code>&lt;outputDirectory&gt;</code> represents where the book will exist when uploaded to the server and should preserve the directory structure in git as referenced in <code>&lt;sourceDirectory&gt;</code>.</p> </div> <div class="paragraph"> <p>Adding Gremlin code examples to any of the <a href="https://github.com/apache/tinkerpop/tree/master/docs/src/recipes">docs/src/recipes</a> or to <a href="https://github.com/apache/tinkerpop/tree/master/docs/src/reference/the-traversal.asciidoc">docs/src/reference/the-traversal.asciidoc</a> also has the effect of improving testing of the Gremlin language. All Gremlin found in code sections that are marked as <code>[gremlin-groovy]</code> are tested in two ways:</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p>When <code>mvn clean install</code> is executed all such Gremlin are passed through the grammar parser to ensure validity. As the grammar parser is not a Groovy parser, the test framework attempts to filter away or ignore things it can&#8217;t possibly parse. Ideally, examples should be written in such a way as to be parsed by the grammar, but in cases where it cannot be as such, the test suite simply needs to be modified to suitably ignore the example.</p> </li> <li> <p>When the documentation is built, the code snippets are actually executed and errors will result in a failure to build the documentation.</p> </li> </ol> </div> <div class="paragraph"> <p>Please see the <a href="#building-testing">Building and Testing</a> section for more information on how to generate the documentation.</p> </div> <div class="sect2"> <h3 id="_asciidoc_formatting_tips">Asciidoc Formatting Tips</h3> <div class="paragraph"> <p><strong>Use Asciidoctor</strong></p> </div> <div class="paragraph"> <p>Asciidoc may render differently with different tools. What may look proper and correct with an IDE may be different than what is ultimately generated during the official build of the documentation which uses Asciidoctor. As a result it&#8217;s best to not rely on any other view of changes besides one generated by Asciidoctor.</p> </div> <div class="paragraph"> <p><strong>Anonymous Traversal Formatting</strong></p> </div> <div class="paragraph"> <p>The double underscore (i.e. <code>__</code>) does not typically render right and requires such code to be wrapped with <code>`pass:[__</code>]` or <code>`+__+`</code>.</p> </div> <div class="paragraph"> <p>Cause: <a href="https://github.com/asciidoctor/asciidoctor/issues/1717">#1717</a>, <a href="https://github.com/asciidoctor/asciidoctor/issues/1066">#1066</a></p> </div> <div class="paragraph"> <p><strong>Non-whitespace After Backtick</strong></p> </div> <div class="paragraph"> <p>Use double backtick if there is non-whitespace immediately following the trailing backtick. So rather than: `ScriptInputFormat`'s, prefer ``ScriptInputFormat``'s.</p> </div> <div class="paragraph"> <p>Original: [&#8230;&#8203;] globally available for <code>ScriptInputFormat&#8217;s `parse()</code> method</p> </div> <div class="paragraph"> <p>Fixed: [&#8230;&#8203;] globally available for <code>ScriptInputFormat</code>'s <code>parse()</code> method</p> </div> <div class="paragraph"> <p>Cause: <a href="https://github.com/asciidoctor/asciidoctor/issues/1514">#1514</a></p> </div> </div> </div> </div> <div class="sect1"> <h2 id="site">Site</h2> <div class="sectionbody"> <div class="paragraph"> <p>The content for the TinkerPop home page and related pages that make up the web site at <a href="//tinkerpop.apache.org">tinkerpop.apache.org</a> is stored in the git repository under <code>/docs/site</code>. In this way, it becomes easier for the community to provide content presented there, because the content can be accepted via the standard workflow of a pull request. To generate the site for local viewing, run <code>bin/generate-home.sh</code>, which will build the site in <code>target/site/</code>. Note that Node.js and npm have to be installed in order for the script to work. See the <a href="#nodejs-environment">JavaScript Environment</a> section for more info about what parts of TinkerPop depend on Node.js and npm. While most of the generated website files can be viewed locally by opening them in a browser, some of them rely on imported resources that will be blocked by the browser&#8217;s same-origin policy if not served from a single origin using a web server. The generated website can be served locally by running <code>npx serve target/site/home</code>. PMC members can officially publish the site with <code>bin/publish-home.sh &lt;username&gt;</code>.</p> </div> <div class="paragraph"> <p>"Publishing" does not publish documentation (e.g. reference docs, javadocs, etc) and only publishes what is generated from the content in <code>/docs/site</code>. Publishing the site can be performed out of band with the release cycle and is no way tied to a version. The <code>master</code> branch should always be considered the "current" web site and publishing should only happen from that branch.</p> </div> </div> </div> <div class="sect1"> <h2 id="logging">Logging</h2> <div class="sectionbody"> <div class="paragraph"> <p>TinkerPop uses SLF4j for logging and relies on logback as the implementation. Configuring log outputs for debugging purposes within tests can be altered by editing the <code>logback-test.xml</code> file in each module&#8217;s test resources. That file gets copied to the <code>target/test-classes</code> on build and surefire and failsafe plugins in maven are then configured to point at that area of the file system for those configuration files. The XML files can be edited to fine tune control of the log output, but generally speaking the current configuration is likely best for everyone&#8217;s general purposes, so if changes are made please revert them prior to commit.</p> </div> </div> </div> <h1 id="_for_providers" class="sect0">For Providers</h1> <div class="openblock partintro"> <div class="content"> Graph Providers are those who develop third-party systems and libraries on top of the various TinkerPop APIs and protocols. They manage their projects independently of TinkerPop in separate repositories. Additional details for providers can be found in the <a href="https://tinkerpop.apache.org/docs/3.7.3/dev/provider/">Provider Documentation</a>. </div> </div> <h1 id="_release_process" class="sect0">Release Process</h1> <div class="openblock partintro"> <div class="content"> <div class="paragraph"> <p>This document describes the steps required to release a version of TinkerPop. The release is handled by a "release manager" (a committer fulfills this role), who ensures that the steps in this document are executed. The process is multi-phased and can therefore take several weeks to complete given the time needed for Apache voting and community feedback. Once a release point has been identified, the following phases represent the flow of "release":</p> </div> <div class="ulist"> <ul> <li> <p>Release manager key setup.</p> </li> <li> <p>Pre-flight check.</p> </li> <li> <p>Optionally, produce a release candidate for community feedback.</p> </li> <li> <p>Submit the official release for PMC vote.</p> </li> <li> <p>Release and promote.</p> </li> </ul> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> During release, it is best to use the current SNAPSHOT version of this documentation to ensure that you have the latest updates as almost every release includes improved documentation that may involve new or revised steps to execute. </td> </tr> </table> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> The following instructions assume that the release manager&#8217;s <a href="#development-environment">environment</a> is setup properly for release and includes a <code>.glv</code> files in the various GLV modules, so that they all build in full. </td> </tr> </table> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> The release process needs to be conducted in a Linux environment. </td> </tr> </table> </div> </div> </div> <div class="sect1"> <h2 id="_development_versions">Development Versions</h2> <div class="sectionbody"> <div class="paragraph"> <p>A "development version" or "snapshot" (in Java parlance) is not an "official" release. Artifacts produced for a snapshot are solely for the convenience of providers and other developers who want to use the latest releases of TinkerPop. These releases do not require a VOTE and do not require a "release manager". Any PMC member can deploy them. It is important to note that these releases cannot be promoted outside of the developer mailing list and should not be recommended for use beyond the purpose of evaluation and testing.</p> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> A development release must contain the suffix "-SNAPSHOT" in the <code>pom.xml</code>. </td> </tr> </table> </div> <div class="paragraph"> <p>For JVM-based artifacts, simply use the following command:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">mvn clean deploy</code></pre> </div> </div> <div class="paragraph"> <p>and artifacts will be pushed to the <a href="https://repository.apache.org/snapshots/">Apache Snapshot Repository</a>. GLV development artifacts must be generated and deployed separately with additional commands:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">mvn clean install -Pglv-python mvn deploy -pl gremlin-python -Dpypi mvn clean install -pl :gremlin-dotnet,:gremlin-dotnet-source -Dnuget mvn deploy -pl :gremlin-dotnet-source -Dnuget mvn deploy -pl gremlin-javascript -Dnpm`</code></pre> </div> </div> <div class="paragraph"> <p>Python, .NET and Javascript do not use the snapshot model the JVM does, however, the build is smart in that it will dynamically generate a development version number for the GLV artifacts when "-SNAPSHOT" is in the <code>pom.xml</code>.</p> </div> <div class="paragraph"> <p>If you wish to verify that <code>mvn deploy</code> works before doing it officially then:</p> </div> <div class="ulist"> <ul> <li> <p>For Python, use the <code>testpypi</code> test environment by updating the <code>gremlin-python/pom.xml</code>.</p> </li> <li> <p>For .NET, use the <code>staging.nuget.org</code> environment by updating the <code>gremlin-dot-source/pom.xml</code></p> </li> </ul> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> The <code>clean</code> in the above commands is more important to the pypi deployment because the process will deploy anything found in the <code>target/python-packaged/dist</code> directory. Since the names of the artifacts are based on timestamps, they will not overwrite one another and multiple artifacts will get uploaded. </td> </tr> </table> </div> <div class="admonitionblock warning"> <table> <tr> <td class="icon"> <div class="title">Warning</div> </td> <td class="content"> These commands will dynamically edit certain files in the various GLVs given automated version number changes. Take care to commit or not commit changes related to that as necessary. </td> </tr> </table> </div> </div> </div> <div class="sect1"> <h2 id="_release_manager_requirements">Release Manager Requirements</h2> <div class="sectionbody"> <div class="paragraph"> <p>If this is your first time as release manager, you will need to set up keys for signing purposes per the Apache release process. Generally speaking, this will mean that you will need to generate a key-pair and then publish your public key.</p> </div> <div class="paragraph"> <p>For a general overview of key basics, refer to <a href="https://www.apache.org/dev/release-signing.html#key-basics">this</a>. For detailed step-by-step instructions, please follow the instructions <a href="https://www.apache.org/dev/openpgp.html#generate-key">here</a>.</p> </div> <div class="paragraph"> <p>After completing the key-pair setup instructions, be sure to upload your public key to <code>keys.openpgp.org</code> and add it into your <a href="https://id.apache.org">Apache LDAP</a> entry (under <code>OpenPGP Public Key Primary Fingerprint</code>). This ensures that the key will be added automatically to this <a href="https://people.apache.org/keys/committer/">list of committer keys</a> which will be used to validate the distribution.</p> </div> <div class="paragraph"> <p>Your public key also needs to be added to KEYS files in both the <a href="https://dist.apache.org/repos/dist/dev/tinkerpop/KEYS">development</a> and <a href="https://dist.apache.org/repos/dist/release/tinkerpop/KEYS">release</a> distribution directories and committed using Apache Subversion (SVN).</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> If you are a committer, you will require the assistance of a PMC member to upload your key into the <a href="https://dist.apache.org/repos/dist/release/tinkerpop/KEYS">release</a> distribution directory. This can also be done during Release &amp; Promote when release files are uploaded. </td> </tr> </table> </div> </div> </div> <div class="sect1"> <h2 id="_pre_flight_check">Pre-flight Check</h2> <div class="sectionbody"> <div class="paragraph"> <p>The "pre-flight check" is a list of things performed by the release manager during the weeks leading up to a scheduled day to release. These checks will help to ensure that that release day goes smoothly by identifying problems up early and communicating with other members of the community.</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p>If this release cycle includes a major release, ensure that all <a href="#runtimes">upgrades for language runtimes</a> have been considered.</p> </li> <li> <p>Fourteen days before release, issue an email to the dev mailing list to remind the community of the pending release.</p> <div class="olist loweralpha"> <ol class="loweralpha" type="a"> <li> <p>Note any important issues open in JIRA in that post.</p> </li> <li> <p>Request review and update of the "upgrade documentation" and CHANGELOG.</p> </li> </ol> </div> </li> <li> <p>Seven days before release, announce the code freeze on the dev mailing list to remind the community that the branch under release is protected. Tweaks to documentation and other odds and ends related to release are still allowed during this period.</p> </li> <li> <p>At some point during the week:</p> <div class="olist loweralpha"> <ol class="loweralpha" type="a"> <li> <p>Run a local build <code>mvn clean install</code></p> </li> <li> <p>Run the full integration test suite: <code>docker/build.sh -t -i -n</code></p> </li> <li> <p>Build and test the Docker images: <code>mvn clean install -pl gremlin-server,gremlin-console -DdockerImages</code></p> </li> <li> <p>Ensure that the Gremlin.Net.Template gets packaged successfully: <code>mvn clean install -pl :gremlin-dotnet-source -Dnuget</code></p> </li> <li> <p>Deploy a final SNAPSHOT to the Apache snapshot repository for Java.</p> </li> <li> <p>Review LICENSE and NOTICE files to make sure that no <a href="#dependencies">changes are needed</a>.</p> </li> <li> <p>Review javadoc filters on the "Core API" docs to be sure nothing needs to change.</p> </li> <li> <p>Review JIRA tickets in the release and ensure that:</p> <div class="olist lowerroman"> <ol class="lowerroman" type="i"> <li> <p>All tickets categorized by having a "Component" assigned.</p> </li> <li> <p>All tickets are either of type "Bug" or "Improvement".</p> </li> <li> <p>All tickets where work was completed are "Closed"</p> <div class="olist upperalpha"> <ol class="upperalpha" type="A"> <li> <p>Search for "closed the pull request" in comments for hints on possible tickets that were left open by mistake.</p> </li> <li> <p>Look for tickets marked as "Resolved" as some users might not have rights to mark as "Closed" - convert these to "Closed".</p> </li> </ol> </div> </li> <li> <p>All tickets not marked "Fixed", "Done", or "Implemented" for their Resolution should not have a Fix Version assigned (use common sense when reviewing these tickets before removing the Fix Version as it is possible the incorrect Resolution may have been assigned).</p> </li> </ol> </div> </li> </ol> </div> </li> <li> <p>When all documentation changes are in place, use <code>bin/publish-docs.sh</code> to deploy a final <code>SNAPSHOT</code> representation of the docs and thus validate that there are no issues with the documentation generation process. Request review of the published documentation on the dev mailing list.</p> </li> </ol> </div> </div> </div> <div class="sect1"> <h2 id="_release_candidate">Release Candidate</h2> <div class="sectionbody"> <div class="paragraph"> <p>A release candidate is an unofficial release that is represented by a tagged version in the Git repository. It is offered in cases where there is significant change in a particular version and the potential for upgrades and problems might be high. Release candidates do not require a vote thread. Lazy consensus is acceptable for confirming their deployment.</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p><code>mvn clean install</code></p> </li> <li> <p><code>docker/build.sh -t -i -n</code></p> </li> <li> <p><code>bin/publish-docs.sh &lt;username&gt;</code> - note that under a release candidate the documentation is published as SNAPSHOT</p> </li> <li> <p><code>mvn versions:set -DnewVersion=xx.yy.zz -DgenerateBackupPoms=false</code> to update the project files to reference a non-SNAPSHOT version</p> </li> <li> <p><code>mvn clean install -DskipTests</code> to update GLV versions</p> <div class="olist loweralpha"> <ol class="loweralpha" type="a"> <li> <p>Review <code>Gremlin.Net.csproj</code>, <code>Gremlin.Net.Template.csproj</code> and <code>Gremlin.Net.Template.nuspec</code> in <code>gremlin-dotnet</code></p> </li> <li> <p>Review <code>package.json</code> and <code>package-lock.json</code> in <code>gremlin-javascript</code> and in <code>gremlint</code></p> </li> </ol> </div> </li> <li> <p><code>pushd gremlin-console/bin; ln -fs ../target/apache-tinkerpop-gremlin-console-xx.yy.zz-standalone/bin/gremlin.sh gremlin.sh; popd</code></p> </li> <li> <p><code>git diff</code> and review the updated files</p> </li> <li> <p><code>git commit -a -m "TinkerPop xx.yy.zz release"</code> and <code>git push</code></p> </li> <li> <p><code>git tag -a -m "TinkerPop xx.yy.zz release" xx.yy.zz</code> and <code>git push --tags</code></p> </li> <li> <p><code>mvn clean install</code></p> </li> <li> <p><code>mvn versions:set -DnewVersion=xx.yy.zz-SNAPSHOT -DgenerateBackupPoms=false</code> to go back to SNAPSHOT</p> </li> <li> <p><code>mvn clean install -DskipTests</code> to update GLV versions</p> <div class="olist loweralpha"> <ol class="loweralpha" type="a"> <li> <p>Review <code>Gremlin.Net.csproj</code>, <code>Gremlin.Net.Template.csproj</code> and <code>Gremlin.Net.Template.nuspec</code> in <code>gremlin-dotnet</code></p> </li> <li> <p>Review <code>package.json</code> and <code>package-lock.json</code> in <code>gremlin-javascript</code> and in <code>gremlint</code></p> </li> </ol> </div> </li> <li> <p><code>pushd gremlin-console/bin; ln -fs ../target/apache-tinkerpop-gremlin-console-xx.yy.zz-SNAPSHOT-standalone/bin/gremlin.sh gremlin.sh; popd</code></p> </li> <li> <p><code>git commit -a -m "Returned to xx.yy.zz-SNAPSHOT"</code> and <code>git push</code></p> </li> <li> <p>Announce the release candidate to <code>dev</code> mailing list and await feedback</p> </li> <li> <p>Repeat as required or proceed to the next phase</p> </li> </ol> </div> <div class="paragraph"> <p>Note that release candidates need not require the release of all artifacts. For example, if the risk is with one particular GLV, then a release candidate can be prepared of just that particular artifact. In those cases, a tag on the commit that represents the release candidate is sufficient and does not necessarily require that the versions be bumped to reflect the actual "-rc" version. In other words, if preparing a release candidate for .NET, then there is no need to go through the processing of bumping versions in Java artifacts and all GLVs. Nor is it necessary to alter the version of .NET to include the release candidate versioning. It can simply be altered locally by the release manager and deployed.</p> </div> </div> </div> <div class="sect1"> <h2 id="_pmc_vote">PMC Vote</h2> <div class="sectionbody"> <div class="paragraph"> <p>This section describes the process that process that prepares a release for VOTE by the community. If there are multiple releases (as there usually are) being prepared, it may be best for downstream releases to wait for upstream releases to complete their process to assure that no last minute commits are required to get the upstream release completed. It is up to the discretion of the release managers to decide how they wish to proceed with respect to preparing releases in parallel or in a more serial fashion.</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p>By this point, the testing performed during the code freeze should have validated the release. If however there are additional tests to perform that the release manager feels are relevant, they should be performed now. In other words, there is no need to rebuild the <code>SNAPSHOT</code> yet another time unless there are circumstances that would call its validity into question.</p> </li> <li> <p>Update <code>CHANGELOG.asciidoc</code>:</p> <div class="olist loweralpha"> <ol class="loweralpha" type="a"> <li> <p>Update the release date - the release date is the date of the vote. Double check the header to be sure that it is formatted properly - specifically, ensure that there is an asciidoc anchor of the format <code><a id="release-x-y-z"></a></code> above the version/date header. The release date is the intended day of the vote and not necessarily when it happens (usually falls on a Monday given the code freeze pattern that starts a week prior on a Friday). The release date for all versions should be the same as the first release of the set even if the release vote does not begin on the same day. In other words if 3.2.x has a vote date of "October, 1 2018" then 3.3.x should also have that date even if the vote thread for 3.3.x doesn&#8217;t start until the day after.</p> </li> <li> <p>Generate the JIRA release notes report for the current version and append them to the <code>CHANGELOG.asciidoc</code>.</p> <div class="olist lowerroman"> <ol class="lowerroman" type="i"> <li> <p>Use an "advanced" search to filter out JIRA issues already released on other versions. For example: <code>project = TINKERPOP and status = Closed AND fixVersion = 3.2.0 ORDER BY type, Id ASC</code>.</p> </li> <li> <p>Consider use of an "Excel" export to organize and prepare the JIRA tickets to be pasted to <code>CHANGELOG.asciidoc</code>. This formula can help construct each line item for the CHANGELOG if column <code>A</code> is the issue number, <code>B</code> is the issue title and <code>D</code> is the label field: <code>="* "&amp;A2&amp;" "&amp;B2&amp;(IF(D2="breaking"," *(breaking)*",""))</code></p> </li> <li> <p>Be sure to include a link to other versions in the <code>CHANGELOG.asciidoc</code> that were previously released while the current release was under development as this new release will have those changes included within it. Please see 3.2.1 for an example.</p> </li> </ol> </div> </li> <li> <p>Format "breaking" changes to be clearly marked (use JIRA and the "breaking" label to identify those - already accounted for if using the suggested formula above)</p> </li> </ol> </div> </li> <li> <p>Update "upgrade documentation":</p> <div class="olist loweralpha"> <ol class="loweralpha" type="a"> <li> <p>Update the release date.</p> </li> <li> <p>Update the link to <code>CHANGELOG.asciidoc</code> - this link may already be correct but will not exist until the repository is tagged.</p> </li> </ol> </div> </li> <li> <p><code>mvn versions:set -DnewVersion=xx.yy.zz -DgenerateBackupPoms=false</code> to update project files to reference the non-SNAPSHOT version</p> </li> <li> <p><code>mvn clean install -DskipTests</code> to update GLV versions</p> <div class="olist loweralpha"> <ol class="loweralpha" type="a"> <li> <p>Review <code>Gremlin.Net.csproj</code>, <code>Gremlin.Net.Template.csproj</code> and <code>Gremlin.Net.Template.nuspec</code> in <code>gremlin-dotnet</code></p> </li> <li> <p>Review <code>package.json</code> and <code>package-lock.json</code> in <code>gremlin-javascript</code> and in <code>gremlint</code></p> </li> </ol> </div> </li> <li> <p><code>pushd gremlin-console/bin; ln -fs ../target/apache-tinkerpop-gremlin-console-xx.yy.zz-standalone/bin/gremlin.sh gremlin.sh; popd</code></p> </li> <li> <p><code>git diff</code> and review the updated files</p> </li> <li> <p><code>mvn clean install</code> - need to build first so that the right version of the console is used with <code>bin/publish-docs.sh</code></p> <div class="olist loweralpha"> <ol class="loweralpha" type="a"> <li> <p>This step should update the Gremlin.Net project file and Gremlin Javascript package file with the newly bumped version.</p> </li> </ol> </div> </li> <li> <p><code>bin/process-docs.sh</code> and validate the generated documentation locally. Don&#8217;t rely on "BUILD SUCCESS" - scroll up through logs to ensure there were no errors and view the HTML directly. Code blocks that did not execute properly have a gray background and do not show the results of the commands. In the event of failure, especially with <code>GraphComputer</code> related code examples, they best recourse is to take the time to test the example manually. While <code>process-docs.sh</code> tries to output the errors it can often point out a red herring where running the example manually will immediately showcase the problem. Unless you immediately know what is wrong, it is almost never a good idea to try to debug the problem based on the error shown by <code>process-docs.sh</code>.</p> </li> <li> <p><code>bin/publish-docs.sh &lt;username&gt;</code> - Note that this step requires no additional processing as the previous step handled document generation and this step now merely needs to upload what was generated. Note that this step will be responsible for generating javadoc.</p> </li> <li> <p><code>mvn deploy -Papache-release -DcreateChecksum=true -DskipTests</code> - deploy signed artifacts with checksums to <a href="https://repository.apache.org/">Apache Nexus</a>.</p> </li> <li> <p>Review generated artifacts (request another committer to review as well) then "close" the repo - if the repo is left open it will be automatically dropped after five days and closing the repo will allow it to stay available for a full ninety days which is more than enough time to complete a vote. Do NOT "release" the repository at this time.</p> </li> <li> <p>Upload artifacts to <code><a href="https://dist.apache.org/repos/dist/dev/tinkerpop" class="bare">https://dist.apache.org/repos/dist/dev/tinkerpop</a></code> for <code>[VOTE]</code> review.</p> <div class="olist loweralpha"> <ol class="loweralpha" type="a"> <li> <p>Use <code>svn rm</code> to delete past versions that were up for review in the same line of code. In other words, if uploading 3.2.3 then remove instances of 3.2.2 or any other past 3.2.x releases.</p> </li> <li> <p><code>svn co --depth empty <a href="https://dist.apache.org/repos/dist/dev/tinkerpop/" class="bare">https://dist.apache.org/repos/dist/dev/tinkerpop/</a> dev</code> and <code>mkdir dev/xx.yy.zz</code></p> </li> <li> <p><code>cp ~/.m2/repository/org/apache/tinkerpop/gremlin-console/xx.yy.zz/gremlin-console-xx.yy.zz-distribution.zip* dev/xx.yy.zz</code></p> </li> <li> <p><code>cp ~/.m2/repository/org/apache/tinkerpop/gremlin-server/xx.yy.zz/gremlin-server-xx.yy.zz-distribution.zip* dev/xx.yy.zz</code></p> </li> <li> <p><code>cp ~/.m2/repository/org/apache/tinkerpop/tinkerpop/xx.yy.zz/tinkerpop-xx.yy.zz-source-release.zip* dev/xx.yy.zz</code></p> </li> <li> <p><code>cd dev/xx.yy.zz</code></p> </li> <li> <p><code>rm -f *.md5</code></p> </li> <li> <p><code>for file in *.sha1 ; do artifact=$(basename ${file} .sha1); sha512sum $artifact | awk '{print $1}' &gt; ${artifact}.sha512; rm ${file}; done</code></p> </li> <li> <p><code>ls * | xargs -n1 -I {} echo "mv apache-tinkerpop-{} {}" | sed -e 's/distribution/bin/' -e 's/source-release/src/' -e 's/tinkerpop-tinkerpop/tinkerpop/' -e s'/^\(.*\) \(.*\) \(.*\)$/\1 \3 \2/' | /bin/bash</code></p> </li> <li> <p><code>cd ..; svn add xx.yy.zz/; svn ci -m "TinkerPop xx.yy.zz release"</code></p> </li> </ol> </div> </li> <li> <p>Execute <code>bin/validate-distribution.sh</code> and any other relevant testing.</p> </li> <li> <p><code>git commit -a -m "TinkerPop xx.yy.zz release"</code> and push</p> </li> <li> <p><code>git tag -a -m "TinkerPop xx.yy.zz release" xx.yy.zz</code> and <code>git push origin xx.yy.zz</code></p> </li> <li> <p>Submit for <code>[VOTE]</code> at <code>dev@tinkerpop.apache.org</code> (see email template below)</p> </li> <li> <p><strong>Wait for vote acceptance</strong> (72 hours)</p> </li> </ol> </div> </div> </div> <div class="sect1"> <h2 id="_release_promote">Release &amp; Promote</h2> <div class="sectionbody"> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> Certain steps can only be performed by a PMC member. If you are a committer, please enlist the help of a PMC member for those steps. </td> </tr> </table> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p>Login to <a href="https://repository.apache.org/">Apache Nexus</a> and release the previously closed repository.</p> </li> <li> <p>Deploy the GLVs</p> <div class="olist loweralpha"> <ol class="loweralpha" type="a"> <li> <p>This build will likely occur from the tag for the release, so be sure to checkout the tag first before executing this step.</p> </li> <li> <p><code>mvn clean install -DskipTests -Dnuget</code></p> </li> <li> <p><code>mvn deploy -pl gremlin-python -DskipTests -Dpypi</code></p> </li> <li> <p><code>mvn deploy -pl :gremlin-dotnet-source -DskipTests -Dnuget</code></p> </li> <li> <p><code>mvn deploy -pl gremlin-javascript -DskipTests -Dnpm</code></p> <div class="olist lowerroman"> <ol class="lowerroman" type="i"> <li> <p>Consider direct deploy with <code>npm publish --otp &lt;otp&gt;</code> from <code>gremlin-javscript/src/main/javascript/gremlin-javascript/</code> - if using this method, ensure the <code>npm</code> version aligns with the one expected by the branch.</p> </li> </ol> </div> </li> <li> <p><code>mvn deploy -pl gremlint -DskipTests -Dnpm</code></p> <div class="olist lowerroman"> <ol class="lowerroman" type="i"> <li> <p>Consider direct deploy with <code>npm publish --otp &lt;otp&gt;</code> from <code>gremlint/</code> - if using this method, ensure the <code>npm</code> version aligns with the one expected by the branch.</p> </li> </ol> </div> </li> <li> <p><code>git tag gremlin-go/v3.6.3</code></p> </li> <li> <p><code>git push origin gremlin-go/v3.6.3</code></p> </li> <li> <p><code>GOPROXY=proxy.golang.org go list -m github.com/apache/tinkerpop/gremlin-go/v3@v3.6.3</code></p> </li> </ol> </div> </li> <li> <p>Review the GLV releases</p> <div class="olist loweralpha"> <ol class="loweralpha" type="a"> <li> <p><a href="https://pypi.org/project/gremlinpython/">gremlin-python - PyPi</a></p> </li> <li> <p><a href="https://www.nuget.org/packages/Gremlin.Net/">Gremlin.Net - nuget</a></p> </li> <li> <p><a href="https://www.nuget.org/packages/Gremlin.Net.Template/">Gremlin.Net.Template - nuget</a></p> </li> <li> <p><a href="https://www.npmjs.com/package/gremlin">gremlin - npm</a></p> </li> <li> <p><a href="https://www.npmjs.com/package/gremlint">gremlint - npm</a></p> </li> <li> <p><a href="https://pkg.go.dev/github.com/apache/tinkerpop/gremlin-go/v3">gremlin-go - go packages</a></p> </li> </ol> </div> </li> <li> <p>Deploy the Docker images <strong>(PMC member)</strong></p> <div class="olist loweralpha"> <ol class="loweralpha" type="a"> <li> <p><code>mvn deploy -pl gremlin-console -DskipTests -DdockerImages</code></p> </li> <li> <p><code>mvn deploy -pl gremlin-server -DskipTests -DdockerImages</code></p> </li> </ol> </div> </li> <li> <p>Review the deployed Docker images at <a href="https://hub.docker.com/r/tinkerpop/gremlin-console/">Console</a> and <a href="https://hub.docker.com/r/tinkerpop/gremlin-server/">Server</a></p> </li> <li> <p><code>svn co --depth empty <a href="https://dist.apache.org/repos/dist/dev/tinkerpop" class="bare">https://dist.apache.org/repos/dist/dev/tinkerpop</a> dev; svn up dev/xx.yy.zz</code> <strong>(PMC member)</strong></p> </li> <li> <p><code>svn co --depth empty <a href="https://dist.apache.org/repos/dist/release/tinkerpop" class="bare">https://dist.apache.org/repos/dist/release/tinkerpop</a> release; mkdir release/xx.yy.zz</code> <strong>(PMC member)</strong></p> </li> <li> <p>Copy release files from <code>dev/xx.yy.zz</code> to <code>release/xx.yy.zz</code>. <strong>(PMC member)</strong></p> </li> <li> <p><code>cd release; svn add xx.yy.zz/; svn ci -m "TinkerPop xx.yy.zz release"</code> <strong>(PMC member)</strong></p> </li> <li> <p>Wait for Apache Sonatype to sync the artifacts to <a href="https://repo1.maven.org/maven2/org/apache/tinkerpop/tinkerpop/">Maven Central</a>.</p> </li> <li> <p>Report the release through <a href="https://reporter.apache.org/addrelease.html?tinkerpop">reporter.apache.org</a> (an email reminder should arrive shortly following the svn command above to do the release)</p> </li> <li> <p>Update homepage with references in <code>/site</code> to latest distribution and to other internal links elsewhere on the page.</p> <div class="olist loweralpha"> <ol class="loweralpha" type="a"> <li> <p>Typically, this need only be done on the <code>master</code> branch as we only publish from there.</p> </li> <li> <p>Update <code>index.html</code>.</p> </li> <li> <p>Update <code>community.html</code>, specifically the version numbers for our official releases.</p> </li> <li> <p>Update <a href="https://tinkerpop.apache.org/download.html">Downloads</a> page, when moving "Current Releases" to "Archived Releases" recall that the hyperlink must change to point to version in the <a href="https://archive.apache.org/dist/tinkerpop/">Apache Archives</a>.</p> </li> <li> <p>Update <code>docs/gremlint/package.json</code> with the latest version of gremlint.</p> </li> <li> <p>Preview changes locally with <code>bin/generate-home.sh</code> then commit changes to git.</p> </li> </ol> </div> </li> <li> <p>Wait for zip distributions to sync to the Apache mirrors (i.e ensure the download links work from a mirror).</p> </li> <li> <p><code>bin/publish-home.sh &lt;username&gt;</code> to publish the updated web site with new releases.</p> </li> <li> <p>Execute <code>bin/update-current-docs.sh &lt;username&gt;</code> to migrate to the latest documentation set for <code>/current</code>.</p> </li> <li> <p>This step should only occur after the website is updated and all links are working. If there are releases present in SVN that represents lines of code that are no longer under development, then remove those releases. In other words, if <code>3.2.0</code> is present and <code>3.2.1</code> is released then remove <code>3.2.0</code>. However, if <code>3.1.3</code> is present and that line of code is still under potential development, it may stay.</p> </li> <li> <p>Announce release - see <a href="#email-templates">templates</a> for suggested form</p> <div class="olist loweralpha"> <ol class="loweralpha" type="a"> <li> <p>Email <code>dev@</code>/<code>gremlin-users@</code> mailing lists</p> </li> <li> <p>Tweet from <code>@apachetinkerpop</code> <strong>(PMC member)</strong></p> </li> <li> <p>Message the "Announcement" Discord channel <strong>(PMC member)</strong></p> </li> </ol> </div> </li> </ol> </div> </div> </div> <div class="sect1"> <h2 id="_post_release_tasks">Post-release Tasks</h2> <div class="sectionbody"> <div class="paragraph"> <p>A number of administration tasks should be taken care of after the release is public. Some of these items can be performed during the VOTE period at the release manager&#8217;s discretion, though it may be wise to wait until a successful VOTE is eminent before reopening development. When there are multiple release managers, it&#8217;s best to coordinate these tasks as one individual may simply just handle them all.</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">Note</div> </td> <td class="content"> Certain steps can only be performed by a PMC member. If you are a committer, please enlist the help of a PMC member for those steps. </td> </tr> </table> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p>Perform JIRA administration tasks <strong>(PMC member)</strong>:</p> <div class="olist loweralpha"> <ol class="loweralpha" type="a"> <li> <p>"Release" the current version and set the "release date"</p> </li> <li> <p>If there is to be a follow on release in the current line of code, create that new version specifying the "start date"</p> </li> </ol> </div> </li> <li> <p>Prepare Git administration tasks. Apply the following steps as needed per release branch:</p> <div class="olist loweralpha"> <ol class="loweralpha" type="a"> <li> <p>Make the appropriate branching changes as required by the release and bump the version to <code>SNAPSHOT</code> with <code>mvn versions:set -DnewVersion=xx.yy.zz-SNAPSHOT -DgenerateBackupPoms=false</code>.</p> </li> </ol> </div> </li> <li> <p><code>mvn clean install -DskipTests</code> to update GLV versions</p> <div class="olist loweralpha"> <ol class="loweralpha" type="a"> <li> <p>Review <code>Gremlin.Net.csproj</code>, <code>Gremlin.Net.Template.csproj</code> and <code>Gremlin.Net.Template.nuspec</code> in <code>gremlin-dotnet</code></p> </li> <li> <p>Review <code>package.json</code> and <code>package-lock.json</code> in <code>gremlin-javascript</code> and in <code>gremlint</code></p> </li> <li> <p><code>pushd gremlin-console/bin; ln -fs ../target/apache-tinkerpop-gremlin-console-xx.yy.zz-SNAPSHOT-standalone/bin/gremlin.sh gremlin.sh; popd</code></p> </li> <li> <p>Update CHANGELOG and upgrade docs to have the appropriate headers for the next version.</p> </li> <li> <p><code>mvn clean install -DskipTests</code> - need to build first so that the right version of the console is used with <code>bin/publish-docs.sh</code></p> </li> <li> <p><code>mvn deploy -DskipTests</code> - deploy the new <code>SNAPSHOT</code></p> </li> <li> <p><code>bin/process-docs.sh</code> and validate the generated <code>SNAPSHOT</code> documentation locally and then <code>bin/publish-docs.sh &lt;username&gt;</code></p> </li> <li> <p>Commit and push the <code>SNAPSHOT</code> changes to git</p> </li> </ol> </div> </li> <li> <p>Examine the <code>future.asciidoc</code> and update the "Roadmap" as needed.</p> </li> <li> <p>Update the version numbers in <code>pull_request_template.md</code>.</p> </li> <li> <p>Send email to advise that code freeze is lifted.</p> </li> <li> <p>Consider the changes made to Gremlin and determine if the community needs to organize a PR to [DefinitelyTyped](<a href="https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/gremlin" class="bare">https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/gremlin</a>) to keep TypeScript up to date.</p> </li> <li> <p>Ensure that the GLV examples compile and run with the latest image and dependencies: <code>bin/run-examples.sh</code>.</p> <div class="olist loweralpha"> <ol class="loweralpha" type="a"> <li> <p>Make changes as necessary to update the examples.</p> </li> </ol> </div> </li> <li> <p>Generate a list of dead branches that will be automatically deleted and post them as a DISCUSS thread for review, then once consensus is reached removed those branches.</p> </li> <li> <p>Set up the IO tests for the current <code>SNAPSHOT</code> as discussed in the <a href="#io">IO Documentation and Testing Section</a></p> </li> </ol> </div> <div class="paragraph"> <p>Apache provides access to download statistics for release <a href="https://logging1-he-de.apache.org/stats/">here</a>.</p> </div> </div> </div> <div class="sect1"> <h2 id="email-templates">Email Templates</h2> <div class="sectionbody"> <div class="sect2"> <h3 id="_release_vote">Release VOTE</h3> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">Subject: [VOTE] TinkerPop xx.yy.zz Release Hello, We are happy to announce that TinkerPop xx.yy.zz is ready for release. The release artifacts can be found at this location: https://dist.apache.org/repos/dist/dev/tinkerpop/xx.yy.zz/ The source distribution is provided by: apache-tinkerpop-xx.yy.zz-src.zip Two binary distributions are provided for user convenience: apache-tinkerpop-gremlin-console-xx.yy.zz-bin.zip apache-tinkerpop-gremlin-server-xx.yy.zz-bin.zip The GPG key used to sign the release artifacts is available at: https://dist.apache.org/repos/dist/dev/tinkerpop/KEYS The online docs can be found here: https://tinkerpop.apache.org/docs/xx.yy.zz/ (user docs) https://tinkerpop.apache.org/docs/xx.yy.zz/upgrade/ (upgrade docs) https://tinkerpop.apache.org/javadocs/xx.yy.zz/core/ (core javadoc) https://tinkerpop.apache.org/javadocs/xx.yy.zz/full/ (full javadoc) https://tinkerpop.apache.org/dotnetdocs/xx.yy.zz/ (.NET API docs) https://tinkerpop.apache.org/jsdocs/xx.yy.zz/ (Javascript API docs) The Git tag can be found here: https://github.com/apache/tinkerpop/tree/xx.yy.zz The release notes are available here: https://github.com/apache/tinkerpop/blob/xx.yy.zz/CHANGELOG.asciidoc The [VOTE] will be open for the next 72 hours --- closing &lt;DayOfTheWeek&gt; (&lt;Month&gt; &lt;Day&gt; &lt;Year&gt;) at &lt;Time&gt; &lt;TimeZone&gt;. My vote is +1. Thank you very much, &lt;TinkerPop Committer Name&gt;</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_release_result_vote">Release RESULT VOTE</h3> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">Subject: [RESULT][VOTE] TinkerPop xx.yy.zz Release This vote is now closed with a total of X +1s, no +0s and no -1s. The results are: BINDING VOTES: +1 (X -- list of voters) 0 (0) -1 (0) NON-BINDING VOTES: +1 (X -- list of voters) 0 (0) -1 (0) Thank you very much, &lt;TinkerPop Committer Name&gt;</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_general_release_announcement">General Release Announcement</h3> <div class="paragraph"> <p>The template below refers to the "name of release line" and the "release line logo". Every release line has a name and logo. For example, 3.1.x had the name, "A 187 On The Undercover Gremlinz" and the logo shown <a href="https://tinkerpop.apache.org/docs/3.7.3/upgrade/#_tinkerpop_3_1_0">here</a> in the upgrade documentation.</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">Subject: Apache TinkerPop xx.yy.zz Released: [name of release line] Hello, Apache TinkerPop xx.yy.zz has just been released. [some text to introduce the release - e.g. whether or not there is breaking change, an important game-changing feature or two, etc.] The release artifacts can be found at this location: https://www.apache.org/dyn/closer.lua/tinkerpop/xx.yy.zz/apache-tinkerpop-gremlin-console-xx.yy.zz-bin.zip https://www.apache.org/dyn/closer.lua/tinkerpop/xx.yy.zz/apache-tinkerpop-gremlin-server-xx.yy.zz-bin.zip https://www.apache.org/dyn/closer.lua/tinkerpop/xx.yy.zz/apache-tinkerpop-xx.yy.zz-src.zip The online docs can be found here: https://tinkerpop.apache.org/docs/xx.yy.zz/reference/ (user docs) https://tinkerpop.apache.org/docs/xx.yy.zz/upgrade/#_tinkerpop_xx_yy_zz (upgrade docs) https://tinkerpop.apache.org/javadocs/xx.yy.zz/core/ (core javadoc) https://tinkerpop.apache.org/javadocs/xx.yy.zz/full/ (full javadoc) https://tinkerpop.apache.org/dotnetdocs/xx.yy.zz/ (.NET API docs) https://tinkerpop.apache.org/jsdocs/xx.yy.zz/ (Javascript API docs) The release notes are available here: https://github.com/apache/tinkerpop/blob/xx.yy.zz/CHANGELOG.asciidoc#release-xx-yy-zz The Central Maven repo has sync'd as well: https://repo1.maven.org/maven2/org/apache/tinkerpop/tinkerpop/xx.yy.zz/ Go artifacts are available via Go Package Manager: https://pkg.go.dev/github.com/apache/tinkerpop/gremlin-go/v3@xx.yy.zz Python artifacts are available in pypi: https://pypi.python.org/pypi/gremlinpython/xx.yy.zz .NET artifacts are available in NuGet: https://www.nuget.org/packages/Gremlin.Net/xx.yy.zz https://www.nuget.org/packages/Gremlin.Net.Template/xx.yy.zz Javascript artifacts are available in npm: https://www.npmjs.com/package/gremlin/v/xx.yy.zz Gremlint is available in npm: https://www.npmjs.com/package/gremlint/v/xx.yy.zz Docker images for Gremlin Console and Gremlin Server can be found on Docker Hub: https://hub.docker.com/u/tinkerpop/ [include the release line logo image]</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_standard_tweet_text">Standard Tweet Text</h3> <div class="paragraph"> <p>The tweet from <code>@apachetinkerpop</code> doesn&#8217;t need to really adhere to any specific format necessarily, but for general patch releases, the following simple text is common:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">Apache TinkerPop xx.yy.zz Released. [name of release] [link to gremlin-users announcement] #graphdb #nosql</code></pre> </div> </div> <div class="paragraph"> <p>Remember to include the picture for the release with the tweet.</p> </div> </div> </div> </div> <h1 id="_administration" class="sect0">Administration</h1> <div class="sect1"> <h2 id="_new_committers">New Committers</h2> <div class="sectionbody"> <div class="paragraph"> <p>When a candidate is identified by a PMC member as someone who might be a good official committer to TinkerPop, the PMC member should open a DISCUSS thread on the private TinkerPop mailing list. The thread should provide some background and reasoning for why that member believes the candidate would be a good committer. Given enough time for feedback on the candidate and presuming there is still positive interest in doing so, a VOTE thread on the private TinkerPop mailing list is started to get the official stance. As per usual, the VOTE will be made open for no less than 72 hours.</p> </div> <div class="paragraph"> <p>If the VOTE closes with a successful positive vote to make the candidate a committer, then send the following email to the candidate and copy the private TinkerPop mailing list:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">SUBJECT: Invitation to become TinkerPop committer: [candidate name] Hello, The TinkerPop Project Management Committee (PMC) hereby offers you committer privileges to the project. These privileges are offered on the understanding that you'll use them reasonably and with common sense. We like to work on trust rather than unnecessary constraints. Being a committer enables you to more easily make changes without needing to go through the patch submission process. Being a committer does not require you to participate any more than you already do. It does tend to make one even more committed. You will probably find that you spend more time here. Of course, you can decline and instead remain as a contributor, participating as you do now. A. This personal invitation is a chance for you to accept or decline in private. Either way, please let us know in reply to the private@tinkerpop.apache.org address only. B. If you are accepting, the next step is to register an iCLA with the Apache Software Foundation: 1. Details of the iCLA and the forms are found through this link: http://www.apache.org/licenses/#clas. 2. The form (text or PDF version) provides instructions for its completion and return to the Secretary of the ASF at secretary@apache.org. 3. When you complete iCLA ensure that you include &quot;Apache TinkerPop&quot; in the &quot;notify project&quot; project field and choose a preferred unique Apache id. Look to see if your preferred id is already taken at http://people.apache.org/committer-index.html. This will allow the Secretary to notify the PMC when your iCLA has been recorded. When recording of your iCLA is noticed, you will receive a follow-up message with the next steps for establishing you as a committer.</code></pre> </div> </div> <div class="paragraph"> <p>Assuming the iCLA included items from step 3, the secretary will handle account creation, otherwise it will be up to the PMC chair to <a href="http://www.apache.org/dev/pmc.html#newcommitter">handle such things</a>. Once the account is established the PMC chair will then need to:</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p>Validate that the iCLA is on file either through <a href="https://svn.apache.org/repos/private/foundation/officers/">svn</a> or through <a href="http://people.apache.org/committer-index.html">people.apache.org</a>.</p> </li> <li> <p>Request that the account be created. The PMC Chair may do this through the <a href="https://whimsy.apache.org/officers/acreq">Account Request Form</a>.</p> </li> <li> <p>Once verified, provide the new committer access to the repository, which is most easily done through <a href="https://whimsy.apache.org/roster/committee/">Whimsy</a>.</p> </li> <li> <p>Send an announcement email to the developer and user mailing lists with the template below.</p> </li> <li> <p>Update JIRA to include this person in the "committers" group.</p> </li> </ol> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">SUBJECT: New Committer: [committer name] The Project Management Committee (PMC) for Apache TinkerPop has asked [committer name] to become a committer and we are pleased to announce their acceptance. [describe the nature of the committer's work in the community] Being a committer enables easier contribution to the project since there is no need to work via the patch submission process. This should enable better productivity.</code></pre> </div> </div> <div class="paragraph"> <p>Finally, new committers should be sent an email that covers some of the administrative elements of their new role. Generally speaking the email can largely just point them to the <a href="#initial-setup">Initial Setup</a> section of this documentation.</p> </div> </div> </div> <div class="sect1"> <h2 id="_new_pmc_members">New PMC Members</h2> <div class="sectionbody"> <div class="paragraph"> <p>The process for bringing on new PMC members is not so different from the one for new committers. The process begins with a DISCUSS thread to the private mailing list for building consensus followed by a VOTE thread to confirm. Presuming the new PMC member is a committer already (which is mostly likely for TinkerPop), there should be no need for any additional paperwork. On successful vote however, a NOTICE should be sent to the <a href="mailto:board@apache.org">board@apache.org</a> (copying <a href="mailto:private@tinkerpop.apache.org">private@tinkerpop.apache.org</a>):</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">SUBJECT: [NOTICE] [name of PMC new member] for Apache TinkerPop PMC Apache TinkerPop proposes to invite [name of PMC new member] ([ApacheId]) to join the PMC. The vote result is available here: [include the link to the vote result thread from private]</code></pre> </div> </div> <div class="paragraph"> <p>If the candidate does not (yet) have an Apache account, then please note that fact in the notification email. It is important to verify that this NOTICE was actually included in the <a href="https://mail-search.apache.org/members/private-arch/board">board archive</a> to ensure that is has been delivered to the board. After verifying this, the invite may be sent to the new PMC member. The following template may be used for the invitation (be sure to copy private@ when sending the email):</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">SUBJECT: Invitation to join the Apache TinkerPop PMC: [name of PMC new member] In recognition of your contributions to Apache TinkerPop, the TinkerPop PMC has voted to make you a PMC member. If you are unfamiliar with what the PMC does, please see this link to be sure you are comfortable with the responsibilities: https://www.apache.org/dev/pmc.html As you have already signed an ICLA and have an Apache account no additional paperwork is required by you other than replying to this email (ensuring to include private@ which is CC'd) to say if you accept or reject the invitation. Obviously there is no requirement to accept - you may simply choose to continue working in the capacity of a committer as you are now. We're happy to answer any questions you might have. Thank you for your efforts on the project.</code></pre> </div> </div> <div class="paragraph"> <p>If they accept, then the PMC chair should refer to the <a href="http://www.apache.org/dev/pmc.html#newpmc">How to Add a New PMC Member</a> section in the Apache docs for how to complete the process. Use <a href="https://whimsy.apache.org/roster/committee/">Whimsy</a> to update PMC membership.</p> </div> </div> </div> <div class="sect1"> <h2 id="_board_reports">Board Reports</h2> <div class="sectionbody"> <div class="paragraph"> <p>The PMC Chair is responsible for submitting a <a href="http://www.apache.org/foundation/board/reporting">report to the board</a> on a quarterly basis. TinkerPop reports on the following months: January, April, July, October.</p> </div> <div class="paragraph"> <p>A draft of the report should be sent to the TinkerPop developer mailing list for review at least three days prior to submitting to the board. The final report can be submitted via <a href="https://whimsy.apache.org/board/agenda/">Whimsy</a> or via the <a href="https://reporter.apache.org">Apache Reporter Service</a> which already provides a template for the report and explains the different sections.</p> </div> </div> </div> <div class="sect1"> <h2 id="contributor-listing">Contributor Listing</h2> <div class="sectionbody"> <div class="paragraph"> <p>Contributions can come in <a href="#ways-to-contribute">many forms</a> and extend well beyond code contributions. TinkerPop strives to ensure that contributions are honestly and accurately recognized for the benefit of the individuals who help make TinkerPop possible.</p> </div> <div class="paragraph"> <p>One of the ways in which this recognition is supported is by way of the <a href="https://tinkerpop.apache.org/#contributors">Contributor Listing</a> on the TinkerPop home page, which list those who have contributed to the project in some way. It lists both committers and PMC members and provides a short "bio" (i.e. a description of their contributions) as well as an indication of whether they are currently participating in the project or not, i.e. active or inactive. The bios have a slightly different form depending on that status:</p> </div> <div class="ulist"> <ul> <li> <p>An <em>active</em> bio should be reflective of current contributions, roughly representing the past six months and the following six months of the update.</p> </li> <li> <p>An <em>inactive</em> bio for those not currently contributing to the project, should reflect the full scope of all contributions made by that individual during their active periods.</p> </li> </ul> </div> <div class="admonitionblock important"> <table> <tr> <td class="icon"> <div class="title">Important</div> </td> <td class="content"> Irrespective of being active or inactive, the contributor&#8217;s name and tenure accomplishments remain present on the front page of the web site. Being "inactive" does <strong>not</strong> affect the contributors status as an Apache committer or PMC member - that remains unchanged. Should a contributor become "inactive" at some point, there is no special process to become "active" again - they simply update their bio to do so. </td> </tr> </table> </div> <div class="paragraph"> <p>Committers and PMC members can keep this information up to date themselves as they have direct access to the Git repository. Reminders to update this information will be sent to the dev list on each release as part of code freeze week. Those who have not shown an update to their bio in git in the past year or have otherwise not replied to the dev list email to indicate their status in that time will be assumed "inactive" for the purpose of the listing.</p> </div> <div class="paragraph"> <p>The following is a template for the "Bio Update" email:</p> </div> <div class="listingblock"> <div class="content"> <pre class="CodeRay highlight"><code data-lang="text">Subject: Apache TinkerPop Bio Update To Committers/PMC Members, As an Apache TinkerPop committer and/or PMC member, your name is listed on the TinkerPop home page in the Contributor List[1] with your &quot;bio&quot;. If you are active on the project, your &quot;bio&quot; reflects what you have been working on and what you expect to be working on with respect to TinkerPop for recent times (i.e. for the previous six months and the following six months). If you are currently inactive on the project, your &quot;bio&quot; reflects the full scope of all your contributions throughout your active periods. You can refer to the contributor listing policy[2] for full details. Please take a moment to update your bio directly in Git[3] or, if you would prefer, please reply to this email with your bio update and it will be added for you. If no changes are required, please reply to this email to confirm that this is the case. [1] https://tinkerpop.apache.org/#contributors [2] https://tinkerpop.apache.org/docs/current/dev/developer/#contributor-listing [3] https://github.com/apache/tinkerpop/blob/master/docs/site/home/index.html</code></pre> </div> </div> </div> </div> <h1 id="meetings" class="sect0">Meetings</h1> <div class="sect1"> <h2 id="_november_23_2015">November 23, 2015</h2> <div class="sectionbody"> <div class="paragraph"> <p>The meeting was scheduled for 12:00pm EST, started at 12:05pm when sufficient attendance to constitute a quorum was reached and was held via Google Hangout hosted by Marko Rodriguez. Meeting adjourned at 2:35pm EST.</p> </div> <div class="paragraph"> <p><strong>Committers/PPMC Members</strong></p> </div> <div class="ulist"> <ul> <li> <p>Daniel Kuppitz</p> </li> <li> <p>Stephen Mallette</p> </li> <li> <p>Dylan Millikin</p> </li> <li> <p>Jason Plurad</p> </li> <li> <p>Marko Rodriguez</p> </li> <li> <p>Ted Wilmes</p> </li> </ul> </div> <div class="paragraph"> <p><strong>Guests</strong></p> </div> <div class="paragraph"> <p><em>None</em></p> </div> <div class="paragraph"> <p><strong>Minutes</strong></p> </div> <div class="ulist"> <ul> <li> <p>Performed a full review of the list of unresolved tickets in JIRA. The summary of the changes can be found below. Note that releases and assignments made today are not meant to be set in stone. The assignments could change given community discussion or unforeseen events.</p> </li> <li> <p>Decided to add version <code>3.2.0-incubating</code> to JIRA. This version was added to help better categorize the available issues. It was explicitly decided that no development was expected to be performed on these issue at this time and that we would wait until <code>3.1.1-incubating</code> was released before thinking too hard on that body of work. This decision means that the <code>master</code> branch will continue to be the current branch of development for the time being and be bound to <code>3.1.x</code> line of code.</p> </li> <li> <p>Discussed use of JIRA dependency links to show "blockers" and dependencies among different JIRA tickets.</p> </li> <li> <p>Discussed a target date for upcoming releases:</p> <div class="ulist"> <ul> <li> <p><code>3.1.1-incubating</code> - February 1, 2016</p> </li> <li> <p><code>3.2.0-incubating</code> - May, 1, 2016</p> </li> </ul> </div> </li> </ul> </div> <div class="paragraph"> <p>The following tickets were assigned to <code>3.1.1-incubating</code>:</p> </div> <table class="tableblock frame-all grid-all stretch"> <colgroup> <col style="width: 22.2222%;"> <col style="width: 55.5555%;"> <col style="width: 22.2223%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">ID</th> <th class="tableblock halign-left valign-top">Description</th> <th class="tableblock halign-left valign-top">Type</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-379</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">MessageScope.Local.setStaticMessage(M msg)</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-659</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">GraphConfiguration Class</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-736</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Automatic Traversal rewriting</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-763</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">IsStep broken when profiling is enabled.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Bug</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-818</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Consider a P.instanceOf()</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-824</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Do we need runtime BigDecimal in more places?</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-842</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">MapReduceHelper should sort respective of the number of reduce tasks</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-859</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Provide a more general way to set log levels in plugins</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-860</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Bindings applied to the PluginAcceptor should appear to Gremlin Server</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-882</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Develop a less error prone way for rewriting strategies</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-886</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Allow any GraphReader/Writer to be persistence engine for TinkerGraph</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-891</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Re-examine Sandboxing Abstractions</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-893</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Validate dependency grabs that have TinkerPop dependencies</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-912</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improve the ability to embed Gremlin Server with Channelizer injection</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-920</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Test case needed for ensuring same cardinality for key.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-927</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">bin/publish-docs.sh should only upload diffs.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-930</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Tie Alias to Transaction Manager in Gremlin Server</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-932</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Add ability to cancel script execution associated with a Gremlin Server Session</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-937</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Move the implementations sections of the primary documentation to "provider tutorials."</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-938</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Add a "clear SNAPSHOT jars" section to the process-docs.sh.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-939</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Neo4jGraph should support HighAvailability (Neo4jHA).</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-941</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improve error message for wrong order().by() arguments</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-943</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Warn if Gremlin Server is running prior to generating docs</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-951</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Barrier steps provide unexpected results in Gremlin OLAP</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Bug</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-954</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Consistent test directory usage</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-956</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Connection errors tend to force a complete close of the channel</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-958</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improve usability of .profile() step.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-964</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Test XXXGraphComputer on a Hadoop2 cluster (non-pseudocluster).</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-967</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Support nested-repeat() structures</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-973</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">BLVP shouldn&#8217;t clear configuration properties</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Bug</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-976</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Fail earlier if invalid version is supplied in validate-distribution.sh</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Bug</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-978</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Native TinkerGraph Serializers for GraphSON</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-979</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">ComputerVerificationStrategy not picking up Order local traversal</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Bug</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-980</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Add a service script or daemon mode in the distribution</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-981</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Deprecate support for credentialsDbLocation in Gremlin Server Config</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-983</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Provide a way to track open Graph instances in tests</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-984</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Use GraphProvider for id conversion in Groovy Environment test suite</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-986</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Manage binary LICENSE/NOTICE files with bin/checkLicenseNotice.sh</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-988</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">SparkGraphComputer.submit shouldn&#8217;t use ForkJoinPool.commonPool</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> </tbody> </table> <div class="paragraph"> <p>The following items were identified for <code>3.2.0-incubating</code>:</p> </div> <table class="tableblock frame-all grid-all stretch"> <colgroup> <col style="width: 22.2222%;"> <col style="width: 55.5555%;"> <col style="width: 22.2223%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">ID</th> <th class="tableblock halign-left valign-top">Description</th> <th class="tableblock halign-left valign-top">Type</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-331</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">TraverserConverterStep (proposal)</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-620</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Commutative Step Marker interface</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-669</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Consider Required TraversalStrategies</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-691</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">TP3 is too prescriptive in exception</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-740</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Serializer Handshake</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-743</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Support "barrier syntax" in step labels.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-761</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Some basic mathematical functions / steps</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-786</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Patterns for DSL Development</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-789</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Choose then Enforce Semantics for Graph.close()</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-790</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Implement AutoCloseable on TraversalSource</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-800</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">[Proposal] Domain/Range checking during traversal construction.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-813</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">[Proposal] Make the Gremlin Graph Traversal Machine and Instruction Set Explicit</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-819</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Mapping Cardinality Interface</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-831</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">How should OLAP treat Collection&lt;Element&gt; objects? No contract is specified.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Bug</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-844</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">PropertyMapStep should reuse PropertiesStep</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-864</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Operator.mean would be nice.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-871</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">RuntimeStrategy as the general model for all such execution time rewrites/re-orders</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-872</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Remove GroupCountStep in favor of new Reduce-based GroupStep</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-873</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">FoldStep should default to using BulkSet for efficiency.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-878</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Refactor Gremlin Server integration tests to be Client parameterized</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-889</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Support for partitioned vertices in GraphComputer</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-890</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Remove the concept of branch/ package.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-919</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Features needs to specify whether 2 vertex properties with same key/value is allowed.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-940</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Convert LocalTraversals to MatchSteps in OLAP and Solve the StarGraph Problem</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-942</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Use EventStrategy to solve OLAP bulk mutation of OLTP.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-944</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Consider deprecating or better enforcing Graph.Exceptions.elementNotFound</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-946</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Traversal respecting Thread.interrupt()</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-947</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Enforce semantics of threaded transactions as manual</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-960</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Add a Bulk class which is used by Traverser</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-962</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Provide "vertex query" selectivity when importing data in OLAP.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-963</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">SubgraphTraversalAnalyzer to determine what is really required from a traversal.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-966</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Support reversible traversals in MatchStep (and respective MatchAlgorithms)</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-969</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">respawn</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-971</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">TraversalSource should be fluent like GraphComputer</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-974</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Saving headless traversals for reuse (clone Iterator Fun)</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-982</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">valuesDecr, valuesIncr, keysDecr, and valuesDecr is lame.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> </tbody> </table> <div class="paragraph"> <p>The following issues were simply closed during review - the reasons for closing can be found in the comments of the issues themselves:</p> </div> <table class="tableblock frame-all grid-all stretch"> <colgroup> <col style="width: 22.2222%;"> <col style="width: 55.5555%;"> <col style="width: 22.2223%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">ID</th> <th class="tableblock halign-left valign-top">Description</th> <th class="tableblock halign-left valign-top">Type</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-375</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Better Methods for Managing ClassPath for Plugins</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-487</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">User Supplied Ids and IO</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-509</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Subgraph support for VertexProgram</p></td> <td class="tableblock halign-left valign-top"></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-604</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">DetachedEdge.attach(Vertex) is too slow.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-795</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Provide Traverser.setPath()</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-862</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Add a TraversalSourceStrategy that provides "locked" values.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-894</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Shorthand for install of TinkerPop dependencies</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-936</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Check feature requirements before opening graph during tests</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Improvement</p></td> </tr> </tbody> </table> </div> </div> <div class="sect1"> <h2 id="_october_29_2015">October 29, 2015</h2> <div class="sectionbody"> <div class="paragraph"> <p>The meeting was scheduled for 1:00pm EST, started on time and was held via Google Hangout hosted by Stephen Mallette. Meeting adjourned at 1:45pm EST.</p> </div> <div class="paragraph"> <p><strong>Committers/PPMC Members</strong></p> </div> <div class="ulist"> <ul> <li> <p>Daniel Kuppitz</p> </li> <li> <p>Stephen Mallette</p> </li> <li> <p>Marko Rodriguez</p> </li> </ul> </div> <div class="paragraph"> <p><strong>Guests</strong></p> </div> <div class="paragraph"> <p><em>None</em></p> </div> <div class="paragraph"> <p><strong>Minutes</strong></p> </div> <div class="ulist"> <ul> <li> <p>Reviewed the scope of 3.1.0-incubating in JIRA in the context of the upcoming release date on November 16, 2015.</p> </li> <li> <p>It was noted that with the new one week code freeze policy that the real cut-off date for work is November 9, 2015.</p> </li> <li> <p>There was general consensus on the call that work should be accomplished in such a way that the code review process not drag into the code freeze time period. In other words, pull requests to the release branch should be completed well ahead of the 9th.</p> </li> </ul> </div> <div class="paragraph"> <p>Upon review of the tickets in JIRA assigned to 3.1.0-incubating, the following were removed from the 3.1.0-incubating roadmap:</p> </div> <table class="tableblock frame-all grid-all stretch"> <colgroup> <col style="width: 16.6666%;"> <col style="width: 41.6666%;"> <col style="width: 41.6668%;"> </colgroup> <thead> <tr> <th class="tableblock halign-left valign-top">ID</th> <th class="tableblock halign-left valign-top">Description</th> <th class="tableblock halign-left valign-top">Removal Reasoning</th> </tr> </thead> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-891</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Re-examine Sandboxing Abstractions</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Low priority and time limitations.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-890</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Remove the concept of branch/ package.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Awaiting step library definition in 3.2.0-incubating.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-873</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">FoldStep should default to using BulkSet for efficiency.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Awaiting step library definition in 3.2.0-incubating.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-864</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Operator.mean would be nice.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Awaiting step library definition in 3.2.0-incubating.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-862</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Add a TraversalSourceStrategy that provides "locked" values.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Low priority and time limitations.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-854</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Remove PropertyMapStep in favor of multi-instruction construction.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Non-trivial given time limitations.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-789</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Choose then Enforce Semantics for Graph.close()</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Non-trivial given time limitations.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-768</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">MatchStep in OLAP should be smart about current vertex.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Non-trivial given time limitations.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-705</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Make use of a MemoryRDD instead of Accumulators in SparkGraphComputer?</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Non-trivial given time limitations.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-659</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">GraphConfiguration Class</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Non-trivial given time limitations.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-623</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">More output for OLAP jobs</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Not clearly defined given time limitations.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-620</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Commutative Step Marker interface</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Awaiting step library definition in 3.2.0-incubating</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-550</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Gremlin IO needs to support both OLTP and OLAP naturally.</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Not clearly defined given time limitations.</p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock">TINKERPOP-479</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Consider Providing "getOrCreate" Functionality</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Not clearly defined given time limitations.</p></td> </tr> </tbody> </table> </div> </div> </div> <div id="footer"> <div id="footer-text"> Last updated 2024-10-24 12:31:42 -0700 </div> </div> </body> </html>

Pages: 1 2 3 4 5 6 7 8 9 10