////
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.
////
= Release Process

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":

* Release manager key setup.
* Pre-flight check.
* Optionally, produce a release candidate for community feedback.
* Submit the official release for PMC vote.
* Release and promote.

IMPORTANT: 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.

IMPORTANT: The following instructions assume that the release manager's <<development-environment,environment>> is setup
properly for release and includes a `.glv` files in the various GLV modules, so that they all build in full.

== Development Versions

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.

IMPORTANT: A development release must contain the suffix "-SNAPSHOT" in the `pom.xml`.

For JVM-based artifacts, simply use the following command:

[source,text]
mvn clean deploy

and artifacts will be pushed to the link:https://repository.apache.org/snapshots/[Apache Snapshot Repository]. GLV
development artifacts must be generated and deployed separately with additional commands:

[source,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`
----

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 `pom.xml`.

If you wish to verify that `mvn deploy` works before doing it officially then:

* For Python, use the `testpypi` test environment by updating the `gremlin-python/pom.xml`.
* For .NET, use the `staging.nuget.org` environment by updating the `gremlin-dot-source/pom.xml`

IMPORTANT: The `clean` in the above commands is more important to the pypi deployment because the process will deploy
anything found in the `target/python-packaged/dist` directory. Since the names of the artifacts are based on
timestamps, they will not overwrite one another and multiple artifacts will get uploaded.

WARNING: 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.

== Release Manager Requirements

If this is your first time as release manager, you will need to setup 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 upload your
public key to a public keyserver.

For a general overview of key basics, refer to link:https://www.apache.org/dev/release-signing.html#key-basics[this].  For detailed
step-by-step instructions, please follow the instructions link:https://www.apache.org/dev/openpgp.html#generate-key[here].

After completing the key-pair setup instructions, be sure to add yourself to the `PGP signature` section of `bin/validate-distribution.sh`.

[source,text]
----
echo -n "  * PGP signature ... "
[ `gpg ${ZIP_FILENAME}.asc 2>&1 | grep -c '^gpg: Good signature from "Stephen Mallette <spmallette@apache.org>"$'` -eq 1 ] || \
[ `gpg ${ZIP_FILENAME}.asc 2>&1 | grep -c '^gpg: Good signature from "Marko Rodriguez <okram@apache.org>"$'` -eq 1 ] || \
[ `gpg ${ZIP_FILENAME}.asc 2>&1 | grep -c '^gpg: Good signature from "Theodore Ratte Wilmes (CODE SIGNING KEY) <twilmes@apache.org>"'` -eq 1 ] || \
{ echo "failed"; exit 1; }
echo "OK"
----

== Pre-flight Check

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.

. Fourteen days before release, issue an email to the dev mailing list to remind the community of the pending release.
.. Note any important issues open in JIRA in that post.
.. Request review and update of the "upgrade documentation" and CHANGELOG.
. 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.
. At some point during the week:
.. Run a local build `mvn clean install`
.. Run the full integration test suite: `docker/build.sh -t -i -n`
.. Build and test the Docker images: `mvn clean install -pl gremlin-server,gremlin-console -DdockerImages`
.. Ensure that the Gremlin.Net.Template gets packaged successfully: `mvn clean install -pl :gremlin-dotnet-source -Dnuget`
.. Deploy a final SNAPSHOT to the Apache snapshot repository for Java.
.. Review LICENSE and NOTICE files to make sure that no <<dependencies,changes are needed>>.
.. Review javadoc filters on the "Core API" docs to be sure nothing needs to change.
.. Review JIRA tickets in the release and ensure that:
... All tickets categorized by having a "Component" assigned.
... All tickets are either of type "Bug" or "Improvement".
... All tickets where work was completed are "Closed"
.... Search for "closed the pull request" in comments for hints on possible tickets that were left open by mistake.
.... Look for tickets marked as "Resolved" as some users might not have rights to mark as "Closed" - convert these to "Closed".
... 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).
.. Be sure that `gremlin-io-test` has been updated (from 3.3.0 on - this module was not available prior to the 3.3.x line)
... This is typically a post-release task, but it's worth checking to be sure in case the step was somehow overlooked.
... Instructions for updating the module are described in the <<io,IO Documentation and Testing Section>>.
. When all documentation changes are in place, use `bin/publish-docs.sh` to deploy a final `SNAPSHOT` 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.

== Release Candidate

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.

. `mvn clean install`
. `docker/build.sh -t -i -n`
. `bin/publish-docs.sh <username>` - note that under a release candidate the documentation is published as SNAPSHOT
. `mvn versions:set -DnewVersion=xx.yy.zz -DgenerateBackupPoms=false` to update the project files to reference a non-SNAPSHOT version
. `pushd gremlin-console/bin; ln -fs ../target/apache-tinkerpop-gremlin-console-xx.yy.zz-standalone/bin/gremlin.sh gremlin.sh; popd`
. `git diff` and review the updated files
. `git commit -a -m "TinkerPop xx.yy.zz release"` and `git push`
. `git tag -a -m "TinkerPop xx.yy.zz release" xx.yy.zz` and `git push --tags`
. `mvn clean install`
. `mvn versions:set -DnewVersion=xx.yy.zz-SNAPSHOT -DgenerateBackupPoms=false` to go back to SNAPSHOT
. `pushd gremlin-console/bin; ln -fs ../target/apache-tinkerpop-gremlin-console-xx.yy.zz-SNAPSHOT-standalone/bin/gremlin.sh gremlin.sh; popd`
. `git commit -a -m "Returned to xx.yy.zz-SNAPSHOT"` and `git push`
. Announce the release candidate to `dev` mailing list and await feedback
. Repeat as required or proceed to the next phase

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.

== PMC Vote

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.

. 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 `SNAPSHOT` yet another time unless there are circumstances that would call its
validity into question.
. Update `CHANGELOG.asciidoc`:
.. 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 - the easiest way to is to view the `CHANGELOG.asciidoc` to be sure that it produces a link in the
GitHub viewer. 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't start until the day after.
.. Generate the JIRA release notes report for the current version and append them to the `CHANGELOG.asciidoc`.
... Use an "advanced" search to filter out JIRA issues already released on other versions. For example:
`project = TINKERPOP and status = Closed AND fixVersion = 3.2.0 ORDER BY type, Id ASC`.
... Consider use of an "Excel" export to organize and prepare the JIRA tickets to be pasted to `CHANGELOG.asciidoc`.
This formula can help construct each line item for the CHANGELOG if column `A` is the issue number, `B` is the
issue title and `D` is the label field: `="* "&A2&" "&B2&(IF(D2="breaking"," \*(breaking)*",""))`
... Be sure to include a link to other versions in the `CHANGELOG.asciidoc` 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.
.. 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)
. Update "upgrade documentation":
.. Update the release date.
.. Update the link to `CHANGELOG.asciidoc` - this link may already be correct but will not exist until the repository is tagged.
. Update homepage with references in `/site` to latest distribution and to other internal links elsewhere on the page.
.. This step should only be performed by the release manager for the newest line of code (i.e. if release 3.3.x, 3.2.x and 3.1.x,
then only do this step for 3.3.x (`3.3-dev` branch), and update the site for all releases).
.. Update the `template/header-footer.html`.
.. Update `index.html`.
.. Update link:https://tinkerpop.apache.org/downloads.html[Downloads] page, when moving "Current Releases" to "Archived
Releases" recall that the hyperlink must change to point to version in the link:https://archive.apache.org/dist/tinkerpop/[Apache Archives]
("contributions" content will be added after VOTE and prior to promotion of the release so that the commit counts are
as accurate as possible and the release tags are present).
.. Preview changes locally with `bin/generate-home.sh` then commit changes to git.
. `mvn versions:set -DnewVersion=xx.yy.zz -DgenerateBackupPoms=false` to update project files to reference the non-SNAPSHOT version
. `pushd gremlin-console/bin; ln -fs ../target/apache-tinkerpop-gremlin-console-xx.yy.zz-standalone/bin/gremlin.sh gremlin.sh; popd`
. `git diff` and review the updated files
. `mvn clean install` - need to build first so that the right version of the console is used with `bin/publish-docs.sh`
.. This step should update the Gremlin.Net project file and Gremlin Javascript package file with the newly bumped version.
. `git commit -a -m "TinkerPop xx.yy.zz release"` and push
. `bin/process-docs.sh` and validate the generated documentation locally. Don'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.
. `bin/publish-docs.sh <username>` - 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 and without that the binary distributions won't contain that documentation.
. `mvn deploy -Papache-release -DcreateChecksum=true -DskipTests` - deploy signed artifacts with checksums to link:https://repository.apache.org/[Apache Nexus].
. Review generated artifacts to be sure they have both javadocs and asciidocs present (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.
. Upload artifacts to `https://dist.apache.org/repos/dist/dev/tinkerpop` for `[VOTE]` review.
.. Use `svn rm` 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.
.. `svn co --depth empty https://dist.apache.org/repos/dist/dev/tinkerpop/ dev` and `mkdir dev/xx.yy.zz`
.. `cp ~/.m2/repository/org/apache/tinkerpop/gremlin-console/xx.yy.zz/gremlin-console-xx.yy.zz-distribution.zip* dev/xx.yy.zz`
.. `cp ~/.m2/repository/org/apache/tinkerpop/gremlin-server/xx.yy.zz/gremlin-server-xx.yy.zz-distribution.zip* dev/xx.yy.zz`
.. `cp ~/.m2/repository/org/apache/tinkerpop/tinkerpop/xx.yy.zz/tinkerpop-xx.yy.zz-source-release.zip* dev/xx.yy.zz`
.. `cd dev/xx.yy.zz`
.. `rm -f *.md5`
.. `for file in *.sha1 ; do artifact=$(basename ${file} .sha1); sha512sum $artifact | awk '{print $1}' > ${artifact}.sha512; rm ${file}; done`
.. pass:[<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>]
.. `cd ..; svn add xx.yy.zz/; svn ci -m "TinkerPop xx.yy.zz release"`
. Execute `bin/validate-distribution.sh` and any other relevant testing.
. `git tag -a -m "TinkerPop xx.yy.zz release" xx.yy.zz` and `git push --tags`
. Submit for `[VOTE]` at `dev@tinkerpop.apache.org` (see email template below)
. *Wait for vote acceptance* (72 hours)

== Release & Promote

. Login to link:https://repository.apache.org/[Apache Nexus] and release the previously closed repository.
. Deploy the GLVs
.. This build will likely occur from the tag for the release, so be sure to checkout the tag first before executing this step.
.. `mvn clean install -DskipTests -Dnuget`
.. `mvn deploy -pl gremlin-python -DskipTests -Dpypi`
.. `mvn deploy -pl :gremlin-dotnet-source -DskipTests -Dnuget`
.. `mvn deploy -pl gremlin-javascript -DskipTests -Dnpm`
. Review the GLV releases
.. link:https://pypi.org/project/gremlinpython/[gremlin-python - PyPi]
.. link:https://www.nuget.org/packages/Gremlin.Net/[Gremlin.Net - nuget]
.. link:https://www.nuget.org/packages/Gremlin.Net.Template/[Gremlin.Net.Template - nuget]
.. link:https://www.npmjs.com/package/gremlin[gremlin - npm]
. Deploy the Docker images
.. `mvn deploy -pl gremlin-console -DskipTests -DdockerImages`
.. `mvn deploy -pl gremlin-server -DskipTests -DdockerImages`
. Review the deployed Docker images at link:https://hub.docker.com/r/tinkerpop/gremlin-console/[Console]
and link:https://hub.docker.com/r/tinkerpop/gremlin-server/[Server]
. `svn co --depth empty https://dist.apache.org/repos/dist/dev/tinkerpop dev; svn up dev/xx.yy.zz`
. `svn co --depth empty https://dist.apache.org/repos/dist/release/tinkerpop release; mkdir release/xx.yy.zz`
. Copy release files from `dev/xx.yy.zz` to `release/xx.yy.zz`.
. `cd release; svn add xx.yy.zz/; svn ci -m "TinkerPop xx.yy.zz release"`
. Wait for Apache Sonatype to sync the artifacts to Maven Central at (link:https://repo1.maven.org/maven2/org/apache/tinkerpop/tinkerpop/[https://repo1.maven.org/maven2/org/apache/tinkerpop/tinkerpop/]).
. Report the release through link:https://reporter.apache.org/addrelease.html?tinkerpop[reporter.apache.org] (an email reminder should arrive shortly following the svn command above to do the release)
. Update the `site/downloads.html` - provide the contents for the modal that pops up from the "contributors" link.
. Wait for zip distributions to sync to the Apache mirrors (i.e ensure the download links work from a mirror).
. `bin/publish-home.sh <username>` to publish the updated web site with new releases.
. Execute `bin/update-current-docs.sh <username>` to migrate to the latest documentation set for `/current`.
. 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 `3.2.0` is present and `3.2.1` is released then remove `3.2.0`.  However, if `3.1.3` is present and that line of
code is still under potential development, it may stay.
. Announce release on `dev@`/`gremlin-users@` mailing lists and tweet from `@apachetinkerpop`

== Post-release Tasks

A number of administration tasks should be taken care of after release is public. Some of these items can be performed
during the VOTE period at the release manager'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's best to coordinate these tasks
as one individual may simply just handle them all.

. Perform JIRA administration tasks:
.. "Release" the current version and set the "release date"
.. If there is to be a follow on release in the current line of code, create that new version specifying the "start date"
. Prepare Git administration tasks. Apply the following steps as needed per release branch:
.. Make the appropriate branching changes as required by the release and bump the version to `SNAPSHOT` with
`mvn versions:set -DnewVersion=xx.yy.zz-SNAPSHOT -DgenerateBackupPoms=false`.
.. `pushd gremlin-console/bin; ln -fs ../target/apache-tinkerpop-gremlin-console-xx.yy.zz-SNAPSHOT-standalone/bin/gremlin.sh gremlin.sh; popd`
.. Update CHANGELOG and upgrade docs to have the appropriate headers for the next version.
.. `mvn clean install -DskipTests` - need to build first so that the right version of the console is used with `bin/publish-docs.sh`
.. `mvn deploy -DskipTests` - deploy the new `SNAPSHOT`
.. `bin/process-docs.sh` and validate the generated `SNAPSHOT` documentation locally and then `bin/publish-docs.sh <username>`
.. Commit and push the `SNAPSHOT` changes to git
. Send email to advise that code freeze is lifted.
. 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.
. Set up the IO tests for the current `SNAPSHOT` as discussed in the <<io,IO Documentation and Testing Section>>

== Email Templates

=== Release VOTE

[source,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 tag in Apache Git 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 <DayOfTheWeek> (<Month> <Day> <Year>) at <Time> <TimeZone>.

My vote is +1.

Thank you very much,
<TinkerPop Committer Name>
----

=== Release RESULT VOTE

[source,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,
<TinkerPop Committer Name>
----

=== General Release Announcement

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
link:https://tinkerpop.apache.org/docs/current/upgrade/#_tinkerpop_3_1_0[here] in the upgrade documentation.

[source,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)
https://tinkerpop.apache.org/docs/xx.yy.zz/some-new-content/ (some new content) [NEW!]

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/

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

Javascript artifacts are available in npm:

https://www.npmjs.com/package/gremlin/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]
----

=== Standard Tweet Text

The tweet from `@apachetinkerpop` doesn't need to really adhere to any specific format necessarily, but for general
patch releases, the following simple text is common:

[source,text]
----
Apache TinkerPop xx.yy.zz Released. [name of release] [link to gremlin-users announcement] #graphdb #nosql
----

Remember to include the picture for the release with the tweet.
