Merge "Fix Eclipse classpath generation for source jars"
diff --git a/.bazelrc b/.bazelrc
index 00acd27..a991c76 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -1 +1 @@
-build --strategy=Javac=worker
+build --workspace_status_command=./tools/workspace-status.sh --strategy=Javac=worker
diff --git a/BUILD b/BUILD
index 538b51c..7ae3589 100644
--- a/BUILD
+++ b/BUILD
@@ -1,11 +1,10 @@
-load('//tools/bzl:genrule2.bzl', 'genrule2')
 load('//tools/bzl:pkg_war.bzl', 'pkg_war')
 
-genrule2(
-  name = 'version',
-  srcs = ['VERSION'],
-  cmd = "grep GERRIT_VERSION $< | cut -d \"'\" -f 2 >$@",
-  out = 'version.txt',
+genrule(
+  name = 'gen_version',
+  stamp = 1,
+  cmd = "grep STABLE_BUILD_GERRIT_LABEL < bazel-out/volatile-status.txt | cut -d ' ' -f 2 > $@",
+  outs = ['version.txt'],
   visibility = ['//visibility:public'],
 )
 
diff --git a/Documentation/BUILD b/Documentation/BUILD
index 98b3ce4..c2acc9c 100644
--- a/Documentation/BUILD
+++ b/Documentation/BUILD
@@ -2,6 +2,30 @@
 load("//tools/bzl:license.bzl", "license_map")
 
 license_map(
-  name = "pgm-licenses",
-  target = "//gerrit-pgm:pgm",
+  name = "licenses",
+  targets = [
+    "//gerrit-pgm:pgm",
+    "//gerrit-gwtui:ui_module",
+  ],
+  opts = ["--asciidoctor"],
+)
+
+DOC_DIR = "Documentation"
+SRCS = glob(["*.txt"])
+
+genrule(
+  name = "index",
+  cmd = "$(location //lib/asciidoctor:doc_indexer) " +
+      "-o $(OUTS) " +
+      '--prefix "%s/" ' % DOC_DIR +
+      '--in-ext ".txt" ' +
+      '--out-ext ".html" ' +
+      "$(SRCS) " +
+      "$(location :licenses.txt)",
+  tools = [
+    ":licenses.txt",
+    "//lib/asciidoctor:doc_indexer",
+  ],
+  srcs = SRCS,
+  outs = ["index.jar"],
 )
diff --git a/WORKSPACE b/WORKSPACE
index 2cade5a..b07f289 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -415,36 +415,36 @@
   sha1 = '18a9a2ce6abf32ea1b5fd31dae5210ad93f4e5e3',
 )
 
-LUCENE_VERS = '5.5.3'
+LUCENE_VERS = '5.5.2'
 
 maven_jar(
   name = 'lucene_core',
   artifact = 'org.apache.lucene:lucene-core:' + LUCENE_VERS,
-  sha1 = '20540c6347259f35a0d264605b22ce2a13917066',
+  sha1 = 'de5e5c3161ea01e89f2a09a14391f9b7ed66cdbb',
 )
 
 maven_jar(
   name = 'lucene_analyzers_common',
   artifact = 'org.apache.lucene:lucene-analyzers-common:' + LUCENE_VERS,
-  sha1 = 'cf734ab72813af33dc1544ce61abc5c17b9d35e9',
+  sha1 = 'f0bc3114a6b43f8e64a33c471d5b9e8ddc51564d',
 )
 
 maven_jar(
   name = 'backward_codecs',
   artifact = 'org.apache.lucene:lucene-backward-codecs:' + LUCENE_VERS,
-  sha1 = 'a167789e52a9dc6d93bf3b588f79fdc9d7559c15',
+  sha1 = 'c5cfcd7a8cf48a0144b61fb991c8e50a0bf868d5',
 )
 
 maven_jar(
   name = 'lucene_misc',
   artifact = 'org.apache.lucene:lucene-misc:' + LUCENE_VERS,
-  sha1 = 'e356975c46447f06c71842632d0af9ec1baecfce',
+  sha1 = '37bbe5a2fb429499dfbe75d750d1778881fff45d',
 )
 
 maven_jar(
   name = 'lucene_queryparser',
   artifact = 'org.apache.lucene:lucene-queryparser:' + LUCENE_VERS,
-  sha1 = 'e2452203d2c44cac5ac42b34e5dcc0a44bf29a53',
+  sha1 = '8ac921563e744463605284c6d9d2d95e1be5b87c',
 )
 
 maven_jar(
@@ -786,3 +786,15 @@
   artifact = 'commons-io:commons-io:1.4',
   sha1 = 'a8762d07e76cfde2395257a5da47ba7c1dbd3dce',
 )
+
+maven_jar(
+  name = "asciidoctor",
+  artifact = "org.asciidoctor:asciidoctorj:1.5.4.1",
+  sha1 = "f7ddfb2bbed2f8da3f9ad0d1a5514f04b4274a5a",
+)
+
+maven_jar(
+  name = "jruby",
+  artifact = "org.jruby:jruby-complete:9.1.5.0",
+  sha1 = "00d0003e99da3c4d830b12c099691ce910c84e39",
+)
diff --git a/gerrit-gwtexpui/BUILD b/gerrit-gwtexpui/BUILD
index d3b03ef..d74fc8b 100644
--- a/gerrit-gwtexpui/BUILD
+++ b/gerrit-gwtexpui/BUILD
@@ -19,6 +19,10 @@
     '//lib/gwt:user',
   ],
   visibility = ['//visibility:public'],
+  data = [
+    '//lib:LICENSE-clippy',
+    '//lib:LICENSE-silk_icons',
+  ],
 )
 
 java_library(
diff --git a/gerrit-gwtui-common/BUILD b/gerrit-gwtui-common/BUILD
index 01a82af..4bd2dfd 100644
--- a/gerrit-gwtui-common/BUILD
+++ b/gerrit-gwtui-common/BUILD
@@ -42,6 +42,10 @@
   name = 'diffy_logo',
   jars = [':diffy_image_files_ln'],
   visibility = ['//visibility:public'],
+  data = [
+    '//lib:LICENSE-diffy',
+    '//lib:LICENSE-CC-BY3.0-unported',
+  ],
 )
 
 genrule2(
diff --git a/gerrit-gwtui/BUILD b/gerrit-gwtui/BUILD
index c223885..cc5da12 100644
--- a/gerrit-gwtui/BUILD
+++ b/gerrit-gwtui/BUILD
@@ -1,5 +1,6 @@
 load('//tools/bzl:gwt.bzl', 'gwt_module')
 load('//tools/bzl:genrule2.bzl', 'genrule2')
+load('//tools/bzl:license.bzl', 'license_test')
 load(':gwt.bzl', 'gwt_binary', 'gwt_genrule', 'gen_ui_module')
 
 gwt_genrule()
@@ -7,3 +8,8 @@
 
 gen_ui_module(name = 'ui_module')
 gen_ui_module(name = 'ui_module', suffix = '_r')
+
+license_test(
+  name = "ui_module_license_test",
+  target = ":ui_module",
+)
diff --git a/gerrit-pgm/BUILD b/gerrit-pgm/BUILD
index 895b5f1..5bdc8fb 100644
--- a/gerrit-pgm/BUILD
+++ b/gerrit-pgm/BUILD
@@ -164,4 +164,5 @@
 
 license_test(
   name = "pgm_license_test",
-  target = ":pgm")
+  target = ":pgm",
+)
diff --git a/gerrit-war/BUILD b/gerrit-war/BUILD
index 86c838f..ae210d4 100644
--- a/gerrit-war/BUILD
+++ b/gerrit-war/BUILD
@@ -62,9 +62,9 @@
   cmd = ' && '.join([
     'cd $$TMP',
     'mkdir -p com/google/gerrit/common',
-    'cat $$ROOT/$(location //:version) >com/google/gerrit/common/Version',
+    'cat $$ROOT/$(location //:version.txt) >com/google/gerrit/common/Version',
     'zip -9Dqr $$ROOT/$@ .',
   ]),
-  tools = ['//:version'],
+  tools = ['//:version.txt'],
   out = 'gen_version.jar',
 )
diff --git a/lib/BUILD b/lib/BUILD
index 3290391..fd25243 100644
--- a/lib/BUILD
+++ b/lib/BUILD
@@ -36,6 +36,12 @@
   "LICENSE-DO_NOT_DISTRIBUTE",
 ])
 
+filegroup(
+  name = 'all-licenses',
+  srcs = glob(['LICENSE-*'], exclude = ['LICENSE-DO_NOT_DISTRIBUTE']),
+  visibility = ['//visibility:public'],
+)
+
 java_library(
   name = 'servlet-api-3_1',
   neverlink = 1,
diff --git a/lib/asciidoctor/BUCK b/lib/asciidoctor/BUCK
index 733c670..5b4cd6b 100644
--- a/lib/asciidoctor/BUCK
+++ b/lib/asciidoctor/BUCK
@@ -53,8 +53,8 @@
 
 maven_jar(
   name = 'jruby',
-  id = 'org.jruby:jruby-complete:1.7.25',
-  sha1 = '8eb234259ec88edc05eedab05655f458a84bfcab',
+  id = 'org.jruby:jruby-complete:9.1.5.0',
+  sha1 = '00d0003e99da3c4d830b12c099691ce910c84e39',
   license = 'DO_NOT_DISTRIBUTE',
   visibility = [],
   attach_source = False,
diff --git a/lib/asciidoctor/BUILD b/lib/asciidoctor/BUILD
new file mode 100644
index 0000000..4b4e958
--- /dev/null
+++ b/lib/asciidoctor/BUILD
@@ -0,0 +1,47 @@
+java_library(
+  name = "asciidoc_lib",
+  srcs = ["java/AsciiDoctor.java"],
+  deps = [
+    ":asciidoctor",
+    "//lib:args4j",
+    "//lib:guava",
+    "//lib/log:api",
+    "//lib/log:nop",
+  ],
+  visibility = ["//visibility:public"],
+)
+
+java_binary(
+  name = "doc_indexer",
+  main_class = "DocIndexer",
+  runtime_deps = [":doc_indexer_lib"],
+  visibility = ["//visibility:public"],
+)
+
+java_library(
+  name = "doc_indexer_lib",
+  srcs = ["java/DocIndexer.java"],
+  deps = [
+    ":asciidoc_lib",
+    "//gerrit-server:constants",
+    "//lib:args4j",
+    "//lib:guava",
+    "//lib/lucene:lucene-analyzers-common",
+    "//lib/lucene:lucene-core-and-backward-codecs",
+  ],
+  visibility = ["//visibility:public"],
+)
+
+java_library(
+  name = "asciidoctor",
+  exports = ["@asciidoctor//jar"],
+  runtime_deps = [":jruby"],
+  visibility = ["//visibility:public"],
+  data = ["//lib:LICENSE-asciidoctor"],
+)
+
+java_library(
+  name = "jruby",
+  exports = ["@jruby//jar"],
+  data = ["//lib:LICENSE-DO_NOT_DISTRIBUTE"],
+)
diff --git a/lib/commons/BUILD b/lib/commons/BUILD
index 68717c2..7f6f6b2 100644
--- a/lib/commons/BUILD
+++ b/lib/commons/BUILD
@@ -52,7 +52,7 @@
   name = 'oro',
   exports = ['@commons_oro//jar'],
   visibility = ['//visibility:public'],
-  data = ['//lib:LICENSE-Apache2.0'],
+  data = ['//lib:LICENSE-Apache1.1'],
 )
 
 java_library(
diff --git a/lib/lucene/BUCK b/lib/lucene/BUCK
index 64dda1d..dee8ce8 100644
--- a/lib/lucene/BUCK
+++ b/lib/lucene/BUCK
@@ -1,6 +1,6 @@
 include_defs('//lib/maven.defs')
 
-VERSION = '5.5.3'
+VERSION = '5.5.2'
 
 # core and backward-codecs both provide
 # META-INF/services/org.apache.lucene.codecs.Codec, so they must be merged.
@@ -16,7 +16,7 @@
 maven_jar(
   name = 'lucene-core',
   id = 'org.apache.lucene:lucene-core:' + VERSION,
-  sha1 = '20540c6347259f35a0d264605b22ce2a13917066',
+  sha1 = 'de5e5c3161ea01e89f2a09a14391f9b7ed66cdbb',
   license = 'Apache2.0',
   exclude = [
     'META-INF/LICENSE.txt',
@@ -28,7 +28,7 @@
 maven_jar(
   name = 'lucene-analyzers-common',
   id = 'org.apache.lucene:lucene-analyzers-common:' + VERSION,
-  sha1 = 'cf734ab72813af33dc1544ce61abc5c17b9d35e9',
+  sha1 = 'f0bc3114a6b43f8e64a33c471d5b9e8ddc51564d',
   license = 'Apache2.0',
   deps = [':lucene-core-and-backward-codecs'],
   exclude = [
@@ -40,7 +40,7 @@
 maven_jar(
   name = 'backward-codecs_jar',
   id = 'org.apache.lucene:lucene-backward-codecs:' + VERSION,
-  sha1 = 'a167789e52a9dc6d93bf3b588f79fdc9d7559c15',
+  sha1 = 'c5cfcd7a8cf48a0144b61fb991c8e50a0bf868d5',
   license = 'Apache2.0',
   deps = [':lucene-core'],
   exclude = [
@@ -53,7 +53,7 @@
 maven_jar(
   name = 'lucene-misc',
   id = 'org.apache.lucene:lucene-misc:' + VERSION,
-  sha1 = 'e356975c46447f06c71842632d0af9ec1baecfce',
+  sha1 = '37bbe5a2fb429499dfbe75d750d1778881fff45d',
   license = 'Apache2.0',
   deps = [':lucene-core-and-backward-codecs'],
   exclude = [
@@ -65,7 +65,7 @@
 maven_jar(
   name = 'lucene-queryparser',
   id = 'org.apache.lucene:lucene-queryparser:' + VERSION,
-  sha1 = 'e2452203d2c44cac5ac42b34e5dcc0a44bf29a53',
+  sha1 = '8ac921563e744463605284c6d9d2d95e1be5b87c',
   license = 'Apache2.0',
   deps = [':lucene-core-and-backward-codecs'],
   exclude = [
diff --git a/tools/bzl/BUILD b/tools/bzl/BUILD
index 01ae92c..bfbbd21 100644
--- a/tools/bzl/BUILD
+++ b/tools/bzl/BUILD
@@ -1,4 +1,6 @@
 
 exports_files([
   "license-map.py",
-  "test_empty.sh"])
+  "test_empty.sh",
+  "test_license.sh",
+])
diff --git a/tools/bzl/license-map.py b/tools/bzl/license-map.py
index 72c7ae8..8469f4f 100644
--- a/tools/bzl/license-map.py
+++ b/tools/bzl/license-map.py
@@ -1,25 +1,146 @@
 #!/usr/bin/env python
 
-# reads a bazel query XML file, to join target names with their licenses.
+# reads bazel query XML files, to join target names with their licenses.
 
-import sys
+from __future__ import print_function
+
+import argparse
+from collections import defaultdict
+from shutil import copyfileobj
+from sys import stdout, stderr
 import xml.etree.ElementTree as ET
 
-tree = ET.parse(sys.argv[1])
-root = tree.getroot()
+KNOWN_PROVIDED_DEPS = [
+  "//lib/bouncycastle:bcpg",
+  "//lib/bouncycastle:bcpkix",
+  "//lib/bouncycastle:bcprov",
+]
 
-entries = {}
+DO_NOT_DISTRIBUTE = "//lib:LICENSE-DO_NOT_DISTRIBUTE"
 
-for child in root:
-  rule_name = child.attrib["name"]
-  for c in child.getchildren():
-    if c.tag != "rule-input":
+LICENSE_PREFIX = "//lib:LICENSE-"
+
+parser = argparse.ArgumentParser()
+parser.add_argument("--asciidoctor", action="store_true")
+parser.add_argument("xmls", nargs="+")
+args = parser.parse_args()
+
+entries = defaultdict(list)
+graph = defaultdict(list)
+handled_rules = []
+
+for xml in args.xmls:
+  tree = ET.parse(xml)
+  root = tree.getroot()
+
+  for child in root:
+    rule_name = child.attrib["name"]
+    if rule_name in handled_rules:
+      # already handled in other xml files
       continue
 
-    license_name = c.attrib["name"]
-    if "//lib:LICENSE" in license_name:
-      assert rule_name not in entries, (license_name, entries[rule_name])
-      entries[rule_name] = license_name
+    handled_rules.append(rule_name)
+    for c in child.getchildren():
+      if c.tag != "rule-input":
+        continue
 
-for k, v in sorted(entries.items()):
-  print k, v
+      license_name = c.attrib["name"]
+      if LICENSE_PREFIX in license_name:
+        if rule_name in KNOWN_PROVIDED_DEPS:
+          continue
+
+        entries[rule_name].append(license_name)
+        graph[license_name].append(rule_name)
+
+if len(graph[DO_NOT_DISTRIBUTE]):
+  print("DO_NOT_DISTRIBUTE license found in:", file=stderr)
+  for target in graph[DO_NOT_DISTRIBUTE]:
+    print(target, file=stderr)
+  exit(1)
+
+if args.asciidoctor:
+  print(
+# We don't want any blank line before "= Gerrit Code Review - Licenses"
+"""= Gerrit Code Review - Licenses
+
+Gerrit open source software is licensed under the <<Apache2_0,Apache
+License 2.0>>.  Executable distributions also include other software
+components that are provided under additional licenses.
+
+[[cryptography]]
+== Cryptography Notice
+
+This distribution includes cryptographic software.  The country
+in which you currently reside may have restrictions on the import,
+possession, use, and/or re-export to another country, of encryption
+software.  BEFORE using any encryption software, please check
+your country's laws, regulations and policies concerning the
+import, possession, or use, and re-export of encryption software,
+to see if this is permitted.  See the
+link:http://www.wassenaar.org/[Wassenaar Arrangement]
+for more information.
+
+The U.S. Government Department of Commerce, Bureau of Industry
+and Security (BIS), has classified this software as Export
+Commodity Control Number (ECCN) 5D002.C.1, which includes
+information security software using or performing cryptographic
+functions with asymmetric algorithms.  The form and manner of
+this distribution makes it eligible for export under the License
+Exception ENC Technology Software Unrestricted (TSU) exception
+(see the BIS Export Administration Regulations, Section 740.13)
+for both object code and source code.
+
+Gerrit includes an SSH daemon (Apache SSHD), to support authenticated
+uploads of changes directly from `git push` command line clients.
+
+Gerrit includes an SSH client (JSch), to support authenticated
+replication of changes to remote systems, such as for automatic
+updates of mirror servers, or realtime backups.
+
+For either feature to function, Gerrit requires the
+link:http://java.sun.com/javase/technologies/security/[Java Cryptography extensions]
+and/or the
+link:http://www.bouncycastle.org/java.html[Bouncy Castle Crypto API]
+to be installed by the end-user.
+
+== Licenses
+""")
+
+  for n in sorted(graph.keys()):
+    if len(graph[n]) == 0:
+      continue
+
+    name = n[len(LICENSE_PREFIX):]
+    safename = name.replace(".", "_")
+    print()
+    print("[[%s]]" % safename)
+    print("=== " + name)
+    print()
+    for d in sorted(graph[n]):
+      if d.startswith("//lib:") or d.startswith("//lib/"):
+        p = d[len("//lib:"):]
+      else:
+        p = d[d.index(":")+1:].lower()
+      if "__" in p:
+        p = p[:p.index("__")]
+      print("* " + p)
+    print()
+    print("[[%s_license]]" % safename)
+    print("----")
+    with open(n[2:].replace(":", "/")) as fd:
+      copyfileobj(fd, stdout)
+    print()
+    print("----")
+    print()
+
+  print(
+"""
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
+""")
+
+else:
+  for k, vs in sorted(entries.items()):
+    for v in vs:
+      print(k, v)
diff --git a/tools/bzl/license.bzl b/tools/bzl/license.bzl
index ca64438..37cc70c 100644
--- a/tools/bzl/license.bzl
+++ b/tools/bzl/license.bzl
@@ -1,47 +1,57 @@
 
-def license_map(name, target):
-    """Generate XML for all targets that depend directly on a LICENSE file"""
+def normalize_target_name(target):
+  return target.replace("//", "").replace("/", "__").replace(":", "___")
+
+def license_map(name, targets = [], opts = []):
+  """Generate XML for all targets that depend directly on a LICENSE file"""
+  xmls = []
+  tools = [ "//tools/bzl:license-map.py", "//lib:all-licenses" ]
+  for target in targets:
+    subname = name + "_" + normalize_target_name(target) + ".xml"
+    xmls.append("$(location :%s)" % subname)
+    tools.append(subname)
     native.genquery(
-        name = name + ".xml",
-        scope = [ target, ],
+      name = subname,
+      scope = [ target ],
 
-        # Find everything that depends on a license file, but remove
-        # the license files themselves from this list.
-        expression = 'rdeps(%s, filter("//lib:LICENSE.*", deps(%s)),1) - filter("//lib:LICENSE.*", deps(%s))' % (target, target, target),
+      # Find everything that depends on a license file, but remove
+      # the license files themselves from this list.
+      expression = 'rdeps(%s, filter("//lib:LICENSE.*", deps(%s)),1) - filter("//lib:LICENSE.*", deps(%s))' % (target, target, target),
 
-        # We are interested in the edges of the graph ({java_library,
-        # license-file} tuples).  'query' provides this in the XML output.
-        opts = [ "--output=xml"],
+      # We are interested in the edges of the graph ({java_library,
+      # license-file} tuples).  'query' provides this in the XML output.
+      opts = [ "--output=xml", ],
     )
 
-    # post process the XML into our favorite format.
-    native.genrule(
-        name = "gen_license_txt_" + name,
-        cmd = "python $(location //tools/bzl:license-map.py) $(location :%s.xml) > $@" % name,
-        outs = [ name + ".txt",],
-        tools = [ "//tools/bzl:license-map.py", name + ".xml"])
+  # post process the XML into our favorite format.
+  native.genrule(
+    name = "gen_license_txt_" + name,
+    cmd = "python $(location //tools/bzl:license-map.py) %s %s > $@" % (" ".join(opts), " ".join(xmls)),
+    outs = [ name + ".txt" ],
+    tools = tools
+  )
 
 def license_test(name, target):
-    """Generate XML for all targets that depend directly on a LICENSE file"""
-    txt = name + "-forbidden.txt"
+  """Make sure a target doesn't depend on DO_NOT_DISTRIBUTE license"""
+  txt = name + "-forbidden.txt"
 
-    # fully qualify target name.
-    if target[0] not in ":/":
-        target = ":" + target
-    if target[0] != "/":
-        target = "//" + PACKAGE_NAME + target
+  # fully qualify target name.
+  if target[0] not in ":/":
+    target = ":" + target
+  if target[0] != "/":
+    target = "//" + PACKAGE_NAME + target
 
-    forbidden = "//lib:LICENSE-DO_NOT_DISTRIBUTE"
-    native.genquery(
-        name = txt,
-        scope = [ target, forbidden ],
-        # Find everything that depends on a license file, but remove
-        # the license files themselves from this list.
-        expression = 'rdeps(%s, "%s", 1) - rdeps(%s, "%s", 0)' % (target, forbidden, target, forbidden),
-    )
-    native.sh_test(
-        name = name,
-        srcs = [ "//tools/bzl:test_empty.sh" ],
-        args  = [ "$(location :%s)" % txt],
-        data = [ txt ],
-    )
+  forbidden = "//lib:LICENSE-DO_NOT_DISTRIBUTE"
+  native.genquery(
+    name = txt,
+    scope = [ target, forbidden ],
+    # Find everything that depends on a license file, but remove
+    # the license files themselves from this list.
+    expression = 'rdeps(%s, "%s", 1) - rdeps(%s, "%s", 0)' % (target, forbidden, target, forbidden),
+  )
+  native.sh_test(
+    name = name,
+    srcs = [ "//tools/bzl:test_license.sh" ],
+    args  = [ "$(location :%s)" % txt ],
+    data = [ txt ],
+  )
diff --git a/tools/bzl/test_license.sh b/tools/bzl/test_license.sh
new file mode 100755
index 0000000..6ac6dab
--- /dev/null
+++ b/tools/bzl/test_license.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+filtered="$1.filtered"
+
+cat $1 \
+  | grep -v "//lib/bouncycastle:bcpg" \
+  | grep -v "//lib/bouncycastle:bcpkix" \
+  | grep -v "//lib/bouncycastle:bcprov" \
+  > $filtered
+
+if test -s $filtered
+then
+  echo "$filtered not empty:"
+  cat $filtered
+  exit 1
+fi
diff --git a/tools/workspace-status.sh b/tools/workspace-status.sh
new file mode 100755
index 0000000..506330c
--- /dev/null
+++ b/tools/workspace-status.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+# This script will be run by bazel when the build process starts to
+# generate key-value information that represents the status of the
+# workspace. The output should be like
+#
+# KEY1 VALUE1
+# KEY2 VALUE2
+#
+# If the script exits with non-zero code, it's considered as a failure
+# and the output will be discarded.
+
+git_rev=$(git describe --always --match "v[0-9].*" --dirty)
+
+echo "STABLE_BUILD_GERRIT_LABEL ${git_rev}"