Merge changes Id03267c3,Ic6021df6

* changes:
  Provide json output from the version programm and SSH command
  Verbose version output, provide noteDb and index versions
diff --git a/Documentation/cmd-version.txt b/Documentation/cmd-version.txt
index cdfc779..3e53678 100644
--- a/Documentation/cmd-version.txt
+++ b/Documentation/cmd-version.txt
@@ -7,6 +7,8 @@
 [verse]
 --
 _ssh_ -p <port> <host> _gerrit version_
+  [--verbose | -v]
+  [--json]
 --
 
 == DESCRIPTION
@@ -31,6 +33,14 @@
 == SCRIPTING
 This command is intended to be used in scripts.
 
+== OPTIONS
+--verbose::
+-v::
+  Verbose output, include also the NoteDb version and the version of each index.
+
+--json::
+  Json output format. Assumes verbose output.
+
 == EXAMPLES
 
 ----
diff --git a/Documentation/rest-api-config.txt b/Documentation/rest-api-config.txt
index fe9b13c..a347d6c 100644
--- a/Documentation/rest-api-config.txt
+++ b/Documentation/rest-api-config.txt
@@ -30,6 +30,32 @@
   "2.7"
 ----
 
+The `verbose` option can be used to provide a verbose version output as
+link:#version-info[VersionInfo].
+
+.Request
+----
+  GET /config/server/version?verbose HTTP/1.0
+----
+
+.Response
+----
+  HTTP/1.1 200 OK
+  Content-Type: application/json; charset=UTF-8
+
+  )]}'
+  {
+    "gerrit_version": "3.8.0",
+    "note_db_version": 185,
+    "change_index_version": 83,
+    "account_index_version": 13,
+    "project_index_version": 6,
+    "group_index_version": 10
+  }
+----
+
+
+
 [[get-info]]
 === Get Server Info
 --
@@ -1968,6 +1994,22 @@
 details.
 |=======================================
 
+[[version-info]]
+=== VersionInfo
+The `VersionInfo` entity contains information about the version of the
+Gerrit server.
+
+[options="header",cols="1,^1,5"]
+|=======================================
+|Field Name                ||Description
+|`gerrit_version`          ||Gerrit server version
+|`note_db_version`         ||NoteDb version
+|`change_index_version`    ||Change index version
+|`account_index_version`   ||Account index version
+|`project_index_version`   ||Project index version
+|`group_index_version`     ||Group index version
+|=======================================
+
 [[server-info]]
 === ServerInfo
 The `ServerInfo` entity contains information about the configuration of
diff --git a/java/com/google/gerrit/extensions/common/VersionInfo.java b/java/com/google/gerrit/extensions/common/VersionInfo.java
new file mode 100644
index 0000000..f18e1cc
--- /dev/null
+++ b/java/com/google/gerrit/extensions/common/VersionInfo.java
@@ -0,0 +1,41 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed 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.
+
+package com.google.gerrit.extensions.common;
+
+public class VersionInfo {
+  public String gerritVersion;
+  public int noteDbVersion;
+  public int changeIndexVersion;
+  public int accountIndexVersion;
+  public int projectIndexVersion;
+  public int groupIndexVersion;
+
+  public String compact() {
+    return "gerrit version " + gerritVersion + "\n";
+  }
+
+  public String verbose() {
+    StringBuilder s = new StringBuilder();
+    s.append("gerrit version " + gerritVersion).append("\n");
+    s.append("NoteDb version " + noteDbVersion).append("\n");
+    s.append("Index versions\n");
+    String format = "  %-8s %3d\n";
+    s.append(String.format(format, "changes", changeIndexVersion));
+    s.append(String.format(format, "accounts", accountIndexVersion));
+    s.append(String.format(format, "projects", projectIndexVersion));
+    s.append(String.format(format, "groups", groupIndexVersion));
+    return s.toString();
+  }
+}
diff --git a/java/com/google/gerrit/pgm/BUILD b/java/com/google/gerrit/pgm/BUILD
index df64bc7..8523e8a 100644
--- a/java/com/google/gerrit/pgm/BUILD
+++ b/java/com/google/gerrit/pgm/BUILD
@@ -20,6 +20,7 @@
         "//java/com/google/gerrit/httpd/auth/restapi",
         "//java/com/google/gerrit/index",
         "//java/com/google/gerrit/index/project",
+        "//java/com/google/gerrit/json",
         "//java/com/google/gerrit/launcher",
         "//java/com/google/gerrit/lifecycle",
         "//java/com/google/gerrit/lucene",
@@ -39,6 +40,7 @@
         "//java/com/google/gerrit/server/restapi",
         "//java/com/google/gerrit/server/schema",
         "//java/com/google/gerrit/server/util/time",
+        "//java/com/google/gerrit/server/version",
         "//java/com/google/gerrit/sshd",
         "//lib:args4j",
         "//lib:guava",
@@ -56,5 +58,6 @@
         "//lib/prolog:cafeteria",
         "//lib/prolog:compiler",
         "//lib/prolog:runtime",
+        "@gson//jar",
     ],
 )
diff --git a/java/com/google/gerrit/pgm/Version.java b/java/com/google/gerrit/pgm/Version.java
index 2392be5..27c52d3 100644
--- a/java/com/google/gerrit/pgm/Version.java
+++ b/java/com/google/gerrit/pgm/Version.java
@@ -14,18 +14,40 @@
 
 package com.google.gerrit.pgm;
 
+import com.google.gerrit.extensions.common.VersionInfo;
+import com.google.gerrit.json.OutputFormat;
 import com.google.gerrit.pgm.util.AbstractProgram;
+import com.google.gerrit.server.version.VersionInfoModule;
+import org.kohsuke.args4j.Option;
 
 /** Display the version of Gerrit. */
 public class Version extends AbstractProgram {
+
+  @Option(
+      name = "--verbose",
+      aliases = {"-v"},
+      usage = "verbose version info")
+  private boolean verbose;
+
+  @Option(name = "--json", usage = "json output format, assumes verbose output")
+  private boolean json;
+
   @Override
   public int run() throws Exception {
-    final String v = com.google.gerrit.common.Version.getVersion();
-    if (v == null) {
+    VersionInfo versionInfo = new VersionInfoModule().createVersionInfo();
+    if (versionInfo.gerritVersion == null) {
       System.err.println("fatal: version unavailable");
       return 1;
     }
-    System.out.println("gerrit version " + v);
+
+    if (json) {
+      System.out.println(OutputFormat.JSON.newGson().toJson(versionInfo));
+    } else if (verbose) {
+      System.out.print(versionInfo.verbose());
+    } else {
+      System.out.print(versionInfo.compact());
+    }
+
     return 0;
   }
 }
diff --git a/java/com/google/gerrit/server/BUILD b/java/com/google/gerrit/server/BUILD
index d68f809..53b6c95 100644
--- a/java/com/google/gerrit/server/BUILD
+++ b/java/com/google/gerrit/server/BUILD
@@ -155,6 +155,7 @@
         "//java/com/google/gerrit/server/logging",
         "//java/com/google/gerrit/server/restapi",
         "//java/com/google/gerrit/server/rules/prolog",
+        "//java/com/google/gerrit/server/version",
         "//lib:blame-cache",
         "//lib:guava",
         "//lib:jgit",
diff --git a/java/com/google/gerrit/server/config/GerritGlobalModule.java b/java/com/google/gerrit/server/config/GerritGlobalModule.java
index e9912f5..f32ba02 100644
--- a/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ b/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -224,6 +224,7 @@
 import com.google.gerrit.server.validators.HashtagValidationListener;
 import com.google.gerrit.server.validators.OutgoingEmailValidationListener;
 import com.google.gerrit.server.validators.ProjectCreationValidationListener;
+import com.google.gerrit.server.version.VersionInfoModule;
 import com.google.gitiles.blame.cache.BlameCache;
 import com.google.gitiles.blame.cache.BlameCacheImpl;
 import com.google.inject.Inject;
@@ -290,6 +291,7 @@
     install(ThreadLocalRequestContext.module());
     install(new ApprovalModule());
     install(new MailSoySauceModule());
+    install(new VersionInfoModule());
 
     factory(CapabilityCollection.Factory.class);
     factory(ChangeData.AssistedFactory.class);
diff --git a/java/com/google/gerrit/server/restapi/config/GetVersion.java b/java/com/google/gerrit/server/restapi/config/GetVersion.java
index ee206d6..6b205e4 100644
--- a/java/com/google/gerrit/server/restapi/config/GetVersion.java
+++ b/java/com/google/gerrit/server/restapi/config/GetVersion.java
@@ -14,23 +14,40 @@
 
 package com.google.gerrit.server.restapi.config;
 
-import com.google.gerrit.common.Version;
+import com.google.gerrit.extensions.common.VersionInfo;
 import com.google.gerrit.extensions.restapi.CacheControl;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.config.ConfigResource;
-import com.google.inject.Singleton;
+import com.google.inject.Inject;
 import java.util.concurrent.TimeUnit;
+import org.kohsuke.args4j.Option;
 
-@Singleton
 public class GetVersion implements RestReadView<ConfigResource> {
+
+  @Option(
+      name = "--verbose",
+      aliases = {"-v"},
+      usage = "verbose version info")
+  boolean verbose;
+
+  private final VersionInfo versionInfo;
+
+  @Inject
+  public GetVersion(VersionInfo versionInfo) {
+    this.versionInfo = versionInfo;
+  }
+
   @Override
-  public Response<String> apply(ConfigResource resource) throws ResourceNotFoundException {
-    String version = Version.getVersion();
-    if (version == null) {
+  public Response<?> apply(ConfigResource resource) throws ResourceNotFoundException {
+    if (versionInfo.gerritVersion == null) {
       throw new ResourceNotFoundException();
     }
-    return Response.ok(version).caching(CacheControl.PRIVATE(30, TimeUnit.SECONDS));
+    if (verbose) {
+      return Response.ok(versionInfo).caching(CacheControl.PRIVATE(30, TimeUnit.SECONDS));
+    }
+    return Response.ok(versionInfo.gerritVersion)
+        .caching(CacheControl.PRIVATE(30, TimeUnit.SECONDS));
   }
 }
diff --git a/java/com/google/gerrit/server/version/BUILD b/java/com/google/gerrit/server/version/BUILD
new file mode 100644
index 0000000..c7f659c
--- /dev/null
+++ b/java/com/google/gerrit/server/version/BUILD
@@ -0,0 +1,17 @@
+load("@rules_java//java:defs.bzl", "java_library")
+
+java_library(
+    name = "version",
+    srcs = glob(
+        ["**/*.java"],
+    ),
+    visibility = ["//visibility:public"],
+    deps = [
+        "//java/com/google/gerrit/common:server",
+        "//java/com/google/gerrit/extensions:api",
+        "//java/com/google/gerrit/index/project",
+        "//java/com/google/gerrit/server",
+        "//java/com/google/gerrit/server/schema",
+        "@guice-library//jar",
+    ],
+)
diff --git a/java/com/google/gerrit/server/version/VersionInfoModule.java b/java/com/google/gerrit/server/version/VersionInfoModule.java
new file mode 100644
index 0000000..e6dea71
--- /dev/null
+++ b/java/com/google/gerrit/server/version/VersionInfoModule.java
@@ -0,0 +1,41 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed 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.
+
+package com.google.gerrit.server.version;
+
+import com.google.gerrit.common.Version;
+import com.google.gerrit.extensions.common.VersionInfo;
+import com.google.gerrit.index.project.ProjectSchemaDefinitions;
+import com.google.gerrit.server.index.account.AccountSchemaDefinitions;
+import com.google.gerrit.server.index.change.ChangeSchemaDefinitions;
+import com.google.gerrit.server.index.group.GroupSchemaDefinitions;
+import com.google.gerrit.server.schema.NoteDbSchemaVersions;
+import com.google.inject.AbstractModule;
+import com.google.inject.Provides;
+import com.google.inject.Singleton;
+
+public class VersionInfoModule extends AbstractModule {
+  @Provides
+  @Singleton
+  public VersionInfo createVersionInfo() {
+    VersionInfo v = new VersionInfo();
+    v.gerritVersion = Version.getVersion();
+    v.noteDbVersion = NoteDbSchemaVersions.LATEST;
+    v.changeIndexVersion = ChangeSchemaDefinitions.INSTANCE.getLatest().getVersion();
+    v.accountIndexVersion = AccountSchemaDefinitions.INSTANCE.getLatest().getVersion();
+    v.projectIndexVersion = ProjectSchemaDefinitions.INSTANCE.getLatest().getVersion();
+    v.groupIndexVersion = GroupSchemaDefinitions.INSTANCE.getLatest().getVersion();
+    return v;
+  }
+}
diff --git a/java/com/google/gerrit/sshd/BUILD b/java/com/google/gerrit/sshd/BUILD
index af7078d..7a11131c 100644
--- a/java/com/google/gerrit/sshd/BUILD
+++ b/java/com/google/gerrit/sshd/BUILD
@@ -21,7 +21,9 @@
         "//java/com/google/gerrit/server/ioutil",
         "//java/com/google/gerrit/server/logging",
         "//java/com/google/gerrit/server/restapi",
+        "//java/com/google/gerrit/server/schema",
         "//java/com/google/gerrit/server/util/time",
+        "//java/com/google/gerrit/server/version",
         "//java/com/google/gerrit/util/cli",
         "//java/com/google/gerrit/util/logging",
         "//lib:args4j",
diff --git a/java/com/google/gerrit/sshd/commands/VersionCommand.java b/java/com/google/gerrit/sshd/commands/VersionCommand.java
index f8771fb..c274b3d 100644
--- a/java/com/google/gerrit/sshd/commands/VersionCommand.java
+++ b/java/com/google/gerrit/sshd/commands/VersionCommand.java
@@ -16,21 +16,40 @@
 
 import static com.google.gerrit.sshd.CommandMetaData.Mode.MASTER_OR_SLAVE;
 
-import com.google.gerrit.common.Version;
+import com.google.gerrit.extensions.common.VersionInfo;
+import com.google.gerrit.json.OutputFormat;
 import com.google.gerrit.sshd.CommandMetaData;
 import com.google.gerrit.sshd.SshCommand;
+import com.google.inject.Inject;
+import org.kohsuke.args4j.Option;
 
 @CommandMetaData(name = "version", description = "Display gerrit version", runsAt = MASTER_OR_SLAVE)
 final class VersionCommand extends SshCommand {
 
+  @Option(
+      name = "--verbose",
+      aliases = {"-v"},
+      usage = "verbose version info")
+  private boolean verbose;
+
+  @Option(name = "--json", usage = "json output format, assumes verbose output")
+  private boolean json;
+
+  @Inject private VersionInfo versionInfo;
+
   @Override
   protected void run() throws Failure {
     enableGracefulStop();
-    String v = Version.getVersion();
-    if (v == null) {
+    if (versionInfo.gerritVersion == null) {
       throw new Failure(1, "fatal: version unavailable");
     }
 
-    stdout.println("gerrit version " + v);
+    if (json) {
+      stdout.println(OutputFormat.JSON.newGson().toJson(versionInfo));
+    } else if (verbose) {
+      stdout.print(versionInfo.verbose());
+    } else {
+      stdout.print(versionInfo.compact());
+    }
   }
 }