This is an automated email from the ASF dual-hosted git repository.

janhoy pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr.git


The following commit(s) were added to refs/heads/main by this push:
     new 34e475784af SOLR-18510: Port AssertTool to picocli (#5036)
34e475784af is described below

commit 34e475784af8ab5f7683c79957a218e0243f4d29
Author: Serhiy Bzhezytskyy <[email protected]>
AuthorDate: Thu Oct 8 18:01:18 2026 +0300

    SOLR-18510: Port AssertTool to picocli (#5036)
---
 .../SOLR-17697-picocli-experimental-cli.yml        |   3 +
 .../src/java/org/apache/solr/cli/AssertTool.java   | 249 ++++++++++++++++++++-
 .../core/src/java/org/apache/solr/cli/SolrCLI.java |   3 +-
 .../org/apache/solr/cli/AssertToolPicocliTest.java |  39 ++++
 .../test/org/apache/solr/cli/AssertToolTest.java   |  23 +-
 solr/packaging/build.gradle                        |   1 +
 .../modules/deployment-guide/deployment-nav.adoc   |   1 +
 .../modules/deployment-guide/pages/cli/index.adoc  |   1 +
 .../deployment-guide/pages/cli/solr-assert.adoc    | 111 +++++++++
 9 files changed, 420 insertions(+), 11 deletions(-)

diff --git a/changelog/unreleased/SOLR-17697-picocli-experimental-cli.yml 
b/changelog/unreleased/SOLR-17697-picocli-experimental-cli.yml
index 85cdf1fd90d..bf2d57cb1f3 100644
--- a/changelog/unreleased/SOLR-17697-picocli-experimental-cli.yml
+++ b/changelog/unreleased/SOLR-17697-picocli-experimental-cli.yml
@@ -7,6 +7,9 @@ type: added
 authors:
   - name: Jan Høydahl
     url: https://home.apache.org/phonebook.html?uid=janhoy
+  - name: Serhiy Bzhezytskyy
 links:
   - name: SOLR-17697
     url: https://issues.apache.org/jira/browse/SOLR-17697
+  - name: SOLR-18510
+    url: https://issues.apache.org/jira/browse/SOLR-18510
diff --git a/solr/core/src/java/org/apache/solr/cli/AssertTool.java 
b/solr/core/src/java/org/apache/solr/cli/AssertTool.java
index 5ba193f9494..c3027b82ed7 100644
--- a/solr/core/src/java/org/apache/solr/cli/AssertTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/AssertTool.java
@@ -39,21 +39,55 @@ import org.slf4j.LoggerFactory;
  * Supports assert command in the bin/solr script. Asserts various conditions 
and exists with error
  * code if there are failures, else continues with no output.
  */
+@SuppressWarnings("UnnecessarilyFullyQualified")
[email protected](
+    name = "assert",
+    description =
+        "Asserts various conditions and exits with an error code if there are 
failures, else"
+            + " continues with no output.",
+    footerHeading = "%nExamples:%n",
+    footer = {
+      "  # Assert Solr is running before continuing",
+      "  bin/solr assert --started http://localhost:8983 --timeout 5000",
+      "",
+      "  # Assert we are not running as root",
+      "  bin/solr assert --not-root"
+    })
 public class AssertTool extends ToolBase {
   private static final Logger log = 
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
   private String message = null;
   private boolean useExitCode = false;
   private Long timeoutMs = 1000L;
 
+  /**
+   * @deprecated Only used by the commons-cli parser; the picocli path 
declares this as an annotated
+   *     field.
+   */
+  @Deprecated
   private static final Option IS_NOT_ROOT_OPTION =
       Option.builder().desc("Asserts that we are NOT the root 
user.").longOpt("not-root").get();
 
+  /**
+   * @deprecated Only used by the commons-cli parser; the picocli path 
declares this as an annotated
+   *     field.
+   */
+  @Deprecated
   private static final Option IS_ROOT_OPTION =
       Option.builder().desc("Asserts that we are the root 
user.").longOpt("root").get();
 
+  /**
+   * @deprecated Only used by the commons-cli parser; the picocli path 
declares this as an annotated
+   *     field.
+   */
+  @Deprecated
   private static final OptionGroup ROOT_OPTION =
       new 
OptionGroup().addOption(IS_NOT_ROOT_OPTION).addOption(IS_ROOT_OPTION);
 
+  /**
+   * @deprecated Only used by the commons-cli parser; the picocli path 
declares this as an annotated
+   *     field.
+   */
+  @Deprecated
   private static final Option IS_NOT_RUNNING_ON_OPTION =
       Option.builder()
           .desc("Asserts that Solr is NOT running on a certain URL. Default 
timeout is 1000ms.")
@@ -62,6 +96,11 @@ public class AssertTool extends ToolBase {
           .argName("url")
           .get();
 
+  /**
+   * @deprecated Only used by the commons-cli parser; the picocli path 
declares this as an annotated
+   *     field.
+   */
+  @Deprecated
   private static final Option IS_RUNNING_ON_OPTION =
       Option.builder()
           .desc("Asserts that Solr is running on a certain URL. Default 
timeout is 1000ms.")
@@ -70,9 +109,19 @@ public class AssertTool extends ToolBase {
           .argName("url")
           .get();
 
+  /**
+   * @deprecated Only used by the commons-cli parser; the picocli path 
declares this as an annotated
+   *     field.
+   */
+  @Deprecated
   private static final OptionGroup RUNNING_OPTION =
       new 
OptionGroup().addOption(IS_NOT_RUNNING_ON_OPTION).addOption(IS_RUNNING_ON_OPTION);
 
+  /**
+   * @deprecated Only used by the commons-cli parser; the picocli path 
declares this as an annotated
+   *     field.
+   */
+  @Deprecated
   private static final Option SAME_USER_OPTION =
       Option.builder()
           .desc("Asserts that we run as same user that owns <directory>.")
@@ -81,6 +130,11 @@ public class AssertTool extends ToolBase {
           .argName("directory")
           .get();
 
+  /**
+   * @deprecated Only used by the commons-cli parser; the picocli path 
declares this as an annotated
+   *     field.
+   */
+  @Deprecated
   private static final Option DIRECTORY_EXISTS_OPTION =
       Option.builder()
           .desc("Asserts that directory <directory> exists.")
@@ -89,6 +143,11 @@ public class AssertTool extends ToolBase {
           .argName("directory")
           .get();
 
+  /**
+   * @deprecated Only used by the commons-cli parser; the picocli path 
declares this as an annotated
+   *     field.
+   */
+  @Deprecated
   private static final Option DIRECTORY_NOT_EXISTS_OPTION =
       Option.builder()
           .desc("Asserts that directory <directory> does NOT exist.")
@@ -97,9 +156,19 @@ public class AssertTool extends ToolBase {
           .argName("directory")
           .get();
 
+  /**
+   * @deprecated Only used by the commons-cli parser; the picocli path 
declares this as an annotated
+   *     field.
+   */
+  @Deprecated
   private static final OptionGroup DIRECTORY_OPTION =
       new 
OptionGroup().addOption(DIRECTORY_EXISTS_OPTION).addOption(DIRECTORY_NOT_EXISTS_OPTION);
 
+  /**
+   * @deprecated Only used by the commons-cli parser; the picocli path 
declares this as an annotated
+   *     field.
+   */
+  @Deprecated
   private static final Option IS_CLOUD_OPTION =
       Option.builder()
           .desc(
@@ -109,6 +178,11 @@ public class AssertTool extends ToolBase {
           .argName("url")
           .get();
 
+  /**
+   * @deprecated Only used by the commons-cli parser; the picocli path 
declares this as an annotated
+   *     field.
+   */
+  @Deprecated
   private static final Option IS_NOT_CLOUD_OPTION =
       Option.builder()
           .desc(
@@ -118,9 +192,19 @@ public class AssertTool extends ToolBase {
           .argName("url")
           .get();
 
+  /**
+   * @deprecated Only used by the commons-cli parser; the picocli path 
declares this as an annotated
+   *     field.
+   */
+  @Deprecated
   private static final OptionGroup CLOUD_OPTION =
       new 
OptionGroup().addOption(IS_CLOUD_OPTION).addOption(IS_NOT_CLOUD_OPTION);
 
+  /**
+   * @deprecated Only used by the commons-cli parser; the picocli path 
declares this as an annotated
+   *     field.
+   */
+  @Deprecated
   private static final Option MESSAGE_OPTION =
       Option.builder()
           .desc("Exception message to be used in place of the default error 
message.")
@@ -129,6 +213,11 @@ public class AssertTool extends ToolBase {
           .argName("message")
           .get();
 
+  /**
+   * @deprecated Only used by the commons-cli parser; the picocli path 
declares this as an annotated
+   *     field.
+   */
+  @Deprecated
   private static final Option TIMEOUT_OPTION =
       Option.builder()
           .desc("Timeout in ms for commands supporting a timeout.")
@@ -138,6 +227,11 @@ public class AssertTool extends ToolBase {
           .argName("ms")
           .get();
 
+  /**
+   * @deprecated Only used by the commons-cli parser; the picocli path 
declares this as an annotated
+   *     field.
+   */
+  @Deprecated
   private static final Option EXIT_CODE_OPTION =
       Option.builder()
           .desc("Return an exit code instead of printing error message on 
assert fail.")
@@ -188,6 +282,109 @@ public class AssertTool extends ToolBase {
       String credentials,
       List<Assertion> assertions) {}
 
+  // --- picocli fields ---
+
+  static class RootOptions {
+    @picocli.CommandLine.Option(
+        names = "--root",
+        description = "Asserts that we are the root user.")
+    boolean isRoot;
+
+    @picocli.CommandLine.Option(
+        names = "--not-root",
+        description = "Asserts that we are NOT the root user.")
+    boolean isNotRoot;
+  }
+
+  static class RunningOptions {
+    @picocli.CommandLine.Option(
+        names = "--started",
+        paramLabel = "url",
+        description = "Asserts that Solr is running on a certain URL. Default 
timeout is 1000ms.")
+    String startedUrl;
+
+    @picocli.CommandLine.Option(
+        names = "--not-started",
+        paramLabel = "url",
+        description =
+            "Asserts that Solr is NOT running on a certain URL. Default 
timeout is 1000ms.")
+    String notStartedUrl;
+  }
+
+  static class DirectoryOptions {
+    @picocli.CommandLine.Option(
+        names = "--exists",
+        paramLabel = "directory",
+        description = "Asserts that directory <directory> exists.")
+    String existsDir;
+
+    @picocli.CommandLine.Option(
+        names = "--not-exists",
+        paramLabel = "directory",
+        description = "Asserts that directory <directory> does NOT exist.")
+    String notExistsDir;
+  }
+
+  static class CloudOptions {
+    @picocli.CommandLine.Option(
+        names = "--cloud",
+        paramLabel = "url",
+        description =
+            "Asserts that Solr is running in cloud mode.  Also fails if Solr 
not running.  URL"
+                + " should be for root Solr path.")
+    String cloudUrl;
+
+    @picocli.CommandLine.Option(
+        names = "--not-cloud",
+        paramLabel = "url",
+        description =
+            "Asserts that Solr is not running in cloud mode.  Also fails if 
Solr not running. "
+                + " URL should be for root Solr path.")
+    String notCloudUrl;
+  }
+
+  @picocli.CommandLine.ArgGroup(exclusive = true, multiplicity = "0..1")
+  private RootOptions rootOptions;
+
+  @picocli.CommandLine.ArgGroup(exclusive = true, multiplicity = "0..1")
+  private RunningOptions runningOptions;
+
+  @picocli.CommandLine.Option(
+      names = "--same-user",
+      paramLabel = "directory",
+      description = "Asserts that we run as same user that owns <directory>.")
+  private String sameUserOpt;
+
+  @picocli.CommandLine.ArgGroup(exclusive = true, multiplicity = "0..1")
+  private DirectoryOptions directoryOptions;
+
+  @picocli.CommandLine.ArgGroup(exclusive = true, multiplicity = "0..1")
+  private CloudOptions cloudOptions;
+
+  @picocli.CommandLine.Option(
+      names = "--message",
+      paramLabel = "message",
+      description = "Exception message to be used in place of the default 
error message.")
+  private String messageOpt;
+
+  @picocli.CommandLine.Option(
+      names = "--timeout",
+      paramLabel = "ms",
+      defaultValue = "1000",
+      description = "Timeout in ms for commands supporting a timeout.")
+  private long timeoutOpt;
+
+  @picocli.CommandLine.Option(
+      names = "--exitcode",
+      description = "Return an exit code instead of printing error message on 
assert fail.")
+  private boolean exitCodeOpt;
+
+  @picocli.CommandLine.Mixin private CredentialsOptions credentialsOptions;
+
+  public AssertTool() {
+    this(new DefaultToolRuntime());
+  }
+
   public AssertTool(ToolRuntime runtime) {
     super(runtime);
   }
@@ -496,7 +693,57 @@ public class AssertTool extends ToolBase {
 
   @Override
   public int callTool() throws Exception {
-    throw new UnsupportedOperationException("This tool does not yet support 
PicoCli");
+    List<Assertion> assertions = new ArrayList<>();
+    if (rootOptions != null && rootOptions.isRoot) {
+      assertions.add(new Assertion.RootUser());
+    }
+    if (rootOptions != null && rootOptions.isNotRoot) {
+      assertions.add(new Assertion.NotRootUser());
+    }
+    if (directoryOptions != null && directoryOptions.existsDir != null) {
+      assertions.add(new Assertion.DirExists(directoryOptions.existsDir));
+    }
+    if (directoryOptions != null && directoryOptions.notExistsDir != null) {
+      assertions.add(new 
Assertion.DirNotExists(directoryOptions.notExistsDir));
+    }
+    if (sameUserOpt != null) {
+      assertions.add(new Assertion.SameUser(sameUserOpt));
+    }
+    if (runningOptions != null && runningOptions.startedUrl != null) {
+      assertions.add(new Assertion.SolrRunning(runningOptions.startedUrl));
+    }
+    if (runningOptions != null && runningOptions.notStartedUrl != null) {
+      assertions.add(new 
Assertion.SolrNotRunning(runningOptions.notStartedUrl));
+    }
+    if (cloudOptions != null && cloudOptions.cloudUrl != null) {
+      assertions.add(new Assertion.CloudMode(cloudOptions.cloudUrl));
+    }
+    if (cloudOptions != null && cloudOptions.notCloudUrl != null) {
+      assertions.add(new Assertion.NotCloudMode(cloudOptions.notCloudUrl));
+    }
+
+    try {
+      return runAssert(
+          new AssertParams(
+              messageOpt,
+              timeoutOpt,
+              exitCodeOpt,
+              credentialsOptions.credentials,
+              List.copyOf(assertions)));
+    } catch (Exception exc) {
+      // Mirrors the commons-cli path's runTool() override: an assertion 
failure or other error
+      // with a message becomes exit code 100, not the ToolBase default of 1.
+      String excMsg = exc.getMessage();
+      if (excMsg == null) {
+        throw exc;
+      }
+      if (isVerbose()) {
+        CLIO.err("\nERROR: " + exc + "\n");
+      } else {
+        CLIO.err("\nERROR: " + excMsg + "\n");
+      }
+      return 100;
+    }
   }
 
   public static class AssertionFailureException extends Exception {
diff --git a/solr/core/src/java/org/apache/solr/cli/SolrCLI.java 
b/solr/core/src/java/org/apache/solr/cli/SolrCLI.java
index a80d7f7883e..62d3475c978 100755
--- a/solr/core/src/java/org/apache/solr/cli/SolrCLI.java
+++ b/solr/core/src/java/org/apache/solr/cli/SolrCLI.java
@@ -80,7 +80,8 @@ import org.slf4j.LoggerFactory;
       ZkTool.class,
       AuthTool.class,
       CreateTool.class,
-      DeleteTool.class
+      DeleteTool.class,
+      AssertTool.class
     })
 public class SolrCLI implements CLIO, java.util.concurrent.Callable<Integer> {
 
diff --git a/solr/core/src/test/org/apache/solr/cli/AssertToolPicocliTest.java 
b/solr/core/src/test/org/apache/solr/cli/AssertToolPicocliTest.java
new file mode 100644
index 00000000000..ba161823455
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/cli/AssertToolPicocliTest.java
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+package org.apache.solr.cli;
+
+import java.util.Arrays;
+import picocli.CommandLine;
+
+/**
+ * Runs all {@link AssertToolTest} tests through the picocli invocation path.
+ *
+ * <p>All {@code @Test} methods are inherited; only the invocation strategy is 
overridden.
+ */
+public class AssertToolPicocliTest extends AssertToolTest {
+
+  @Override
+  protected int runTool(String[] args, Class<? extends ToolBase> clazz) throws 
Exception {
+    // args[0] is the tool name used by commons-cli dispatch; strip it for 
picocli.
+    String[] toolArgs = Arrays.copyOfRange(args, 1, args.length);
+    ToolRuntime runtime = new CLITestHelper.TestingRuntime(false);
+    ToolBase tool = 
clazz.getDeclaredConstructor(ToolRuntime.class).newInstance(runtime);
+    return new CommandLine(tool)
+        .setDefaultValueProvider(new CliDefaultValueProvider())
+        .execute(toolArgs);
+  }
+}
diff --git a/solr/core/src/test/org/apache/solr/cli/AssertToolTest.java 
b/solr/core/src/test/org/apache/solr/cli/AssertToolTest.java
index 9f3aa0e77a5..77f6a273108 100644
--- a/solr/core/src/test/org/apache/solr/cli/AssertToolTest.java
+++ b/solr/core/src/test/org/apache/solr/cli/AssertToolTest.java
@@ -28,6 +28,11 @@ import org.junit.Test;
 
 public class AssertToolTest extends SolrCloudTestCase {
 
+  /** Runs the tool. Overridden by the picocli variant of this test. */
+  protected int runTool(String[] args, Class<? extends ToolBase> clazz) throws 
Exception {
+    return CLITestHelper.runTool(args, clazz);
+  }
+
   @BeforeClass
   public static void setupCluster() throws Exception {
     configureCluster(1).addConfig("conf", 
configset("cloud-minimal")).configure();
@@ -42,7 +47,7 @@ public class AssertToolTest extends SolrCloudTestCase {
         System.getSecurityManager() != null);
     final String[] args = new String[] {"assert", "--exitcode", "--exists", 
"/tmp"};
 
-    final int numAssertionsFailed = CLITestHelper.runTool(args, 
AssertTool.class);
+    final int numAssertionsFailed = runTool(args, AssertTool.class);
 
     assertEquals("Expected AssertTool to raise an error", 100, 
numAssertionsFailed);
   }
@@ -52,7 +57,7 @@ public class AssertToolTest extends SolrCloudTestCase {
     Path tempDir = Files.createTempDirectory("myTempDir");
     final String[] args = new String[] {"assert", "--exitcode", "--exists", 
tempDir.toString()};
 
-    final int numAssertionsFailed = CLITestHelper.runTool(args, 
AssertTool.class);
+    final int numAssertionsFailed = runTool(args, AssertTool.class);
 
     assertEquals(
         "Expected AssertTool to pass assertion that directory exists", 0, 
numAssertionsFailed);
@@ -66,7 +71,7 @@ public class AssertToolTest extends SolrCloudTestCase {
     final String[] args =
         new String[] {"assert", "--exitcode", "--exists", tempDir.toString() + 
"/foo/bar/baz"};
 
-    final int numAssertionsFailed = CLITestHelper.runTool(args, 
AssertTool.class);
+    final int numAssertionsFailed = runTool(args, AssertTool.class);
 
     assertEquals(
         "Expected AssertTool to fail assertion that directory exists", 1, 
numAssertionsFailed);
@@ -77,7 +82,7 @@ public class AssertToolTest extends SolrCloudTestCase {
     Path tempDir = Files.createTempDirectory("myTempDir");
     final String[] args = new String[] {"assert", "--exitcode", 
"--not-exists", tempDir.toString()};
 
-    final int numAssertionsFailed = CLITestHelper.runTool(args, 
AssertTool.class);
+    final int numAssertionsFailed = runTool(args, AssertTool.class);
 
     assertEquals(
         "Expected AssertTool to fail assertion that directory doesnt exist",
@@ -91,7 +96,7 @@ public class AssertToolTest extends SolrCloudTestCase {
     final String[] args =
         new String[] {"assert", "--exitcode", "--not-exists", 
tempDir.toString() + "/foo/bar/baz"};
 
-    final int numAssertionsFailed = CLITestHelper.runTool(args, 
AssertTool.class);
+    final int numAssertionsFailed = runTool(args, AssertTool.class);
 
     assertEquals(
         "Expected AssertTool to fail assertion that directory doesnt exist",
@@ -104,7 +109,7 @@ public class AssertToolTest extends SolrCloudTestCase {
     final String baseUrl = getRealSolrBaseUrl();
     final String[] args = new String[] {"assert", "--exitcode", "--started", 
baseUrl};
 
-    final int numAssertionsFailed = CLITestHelper.runTool(args, 
AssertTool.class);
+    final int numAssertionsFailed = runTool(args, AssertTool.class);
 
     assertEquals(
         "Expected AssertTool to pass assertion when Solr is running on 
provided URL",
@@ -117,7 +122,7 @@ public class AssertToolTest extends SolrCloudTestCase {
     final String[] args =
         new String[] {"assert", "--exitcode", "--started", 
"http://www.google.com"};
 
-    final int numAssertionsFailed = CLITestHelper.runTool(args, 
AssertTool.class);
+    final int numAssertionsFailed = runTool(args, AssertTool.class);
 
     assertEquals(
         "Expected AssertTool to fail assertion when Solr isn't running on 
provided URL",
@@ -130,7 +135,7 @@ public class AssertToolTest extends SolrCloudTestCase {
     final String baseUrl = getRealSolrBaseUrl();
     final String[] args = new String[] {"assert", "--exitcode", 
"--not-started", baseUrl};
 
-    final int numAssertionsFailed = CLITestHelper.runTool(args, 
AssertTool.class);
+    final int numAssertionsFailed = runTool(args, AssertTool.class);
 
     assertEquals(
         "Expected AssertTool to fail assertion when Solr is running on 
provided URL",
@@ -143,7 +148,7 @@ public class AssertToolTest extends SolrCloudTestCase {
     final String[] args =
         new String[] {"assert", "--exitcode", "--not-started", 
"http://www.google.com"};
 
-    final int numAssertionsFailed = CLITestHelper.runTool(args, 
AssertTool.class);
+    final int numAssertionsFailed = runTool(args, AssertTool.class);
 
     assertEquals(
         "Expected AssertTool to pass assertion when Solr isn't running on 
provided URL",
diff --git a/solr/packaging/build.gradle b/solr/packaging/build.gradle
index 9672174b2a4..ccdffbeeaf4 100644
--- a/solr/packaging/build.gradle
+++ b/solr/packaging/build.gradle
@@ -363,6 +363,7 @@ task integrationTestsPicocli(type: BatsTask) {
       'test/test_create.bats',
       'test/test_delete_collection.bats',
       'test/test_zk.bats',
+      'test/test_auth.bats',
   ]
 }
 
diff --git a/solr/solr-ref-guide/modules/deployment-guide/deployment-nav.adoc 
b/solr/solr-ref-guide/modules/deployment-guide/deployment-nav.adoc
index 309b2bd959f..53a667155ef 100644
--- a/solr/solr-ref-guide/modules/deployment-guide/deployment-nav.adoc
+++ b/solr/solr-ref-guide/modules/deployment-guide/deployment-nav.adoc
@@ -20,6 +20,7 @@
 * xref:solr-control-script-reference.adoc[]
 ** xref:cli/index.adoc[Experimental picocli CLI]
 // CLI-DOCS-START
+*** xref:cli/solr-assert.adoc[assert]
 *** xref:cli/solr-auth.adoc[auth]
 *** xref:cli/solr-create.adoc[create]
 *** xref:cli/solr-delete.adoc[delete]
diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/cli/index.adoc 
b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/index.adoc
index f0010959806..1690291943b 100644
--- a/solr/solr-ref-guide/modules/deployment-guide/pages/cli/index.adoc
+++ b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/index.adoc
@@ -59,6 +59,7 @@ Any command not listed here is unavailable when the picocli 
implementation is en
 |===
 |Command
 
+|xref:cli/solr-assert.adoc[bin/solr assert]
 |xref:cli/solr-auth.adoc[bin/solr auth]
 |xref:cli/solr-create.adoc[bin/solr create]
 |xref:cli/solr-delete.adoc[bin/solr delete]
diff --git 
a/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-assert.adoc 
b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-assert.adoc
new file mode 100644
index 00000000000..39c13bf8206
--- /dev/null
+++ b/solr/solr-ref-guide/modules/deployment-guide/pages/cli/solr-assert.adoc
@@ -0,0 +1,111 @@
+// 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.
+//
+// DO NOT EDIT -- this page is auto-generated from picocli annotations.
+// To update: modify the @Command/@Option annotations in the Java source, then 
run:
+//   ./gradlew :solr:solr-ref-guide:generateCliDocs
+
+= bin/solr assert
+:page-toclevels: 2
+
+[WARNING]
+====
+This page documents the *experimental* picocli implementation of `bin/solr`, 
which is not enabled by default.
+For what `bin/solr` does by default, see 
xref:solr-control-script-reference.adoc[].
+See xref:cli/index.adoc[] to enable this implementation and for its known 
limitations.
+====
+
+// tag::picocli-generated-man-section-synopsis[]
+== Synopsis
+
+....
+bin/solr assert [-hv] [--exitcode] [--message=message] [--same-user=directory]
+                [--timeout=ms] [-u=<credentials>] [--root | --not-root]
+                [--started=url | --not-started=url] [--exists=directory |
+                --not-exists=directory] [--cloud=url | --not-cloud=url]
+....
+
+// end::picocli-generated-man-section-synopsis[]
+
+// tag::picocli-generated-man-section-description[]
+== Description
+
+Asserts various conditions and exits with an error code if there are failures, 
else continues with no output.
+
+// end::picocli-generated-man-section-description[]
+
+// tag::picocli-generated-man-section-options[]
+== Options
+
+*--cloud*=_url_::
+  Asserts that Solr is running in cloud mode.  Also fails if Solr not running. 
 URL should be for root Solr path.
+
+*--exists*=_directory_::
+  Asserts that directory <directory> exists.
+
+*--exitcode*::
+  Return an exit code instead of printing error message on assert fail.
+
+*-h*, *--help*::
+  Print this help message and exit.
+
+*--message*=_message_::
+  Exception message to be used in place of the default error message.
+
+*--not-cloud*=_url_::
+  Asserts that Solr is not running in cloud mode.  Also fails if Solr not 
running.  URL should be for root Solr path.
+
+*--not-exists*=_directory_::
+  Asserts that directory <directory> does NOT exist.
+
+*--not-root*::
+  Asserts that we are NOT the root user.
+
+*--not-started*=_url_::
+  Asserts that Solr is NOT running on a certain URL. Default timeout is 1000ms.
+
+*--root*::
+  Asserts that we are the root user.
+
+*--same-user*=_directory_::
+  Asserts that we run as same user that owns <directory>.
+
+*--started*=_url_::
+  Asserts that Solr is running on a certain URL. Default timeout is 1000ms.
+
+*--timeout*=_ms_::
+  Timeout in ms for commands supporting a timeout.
+
+*-u*, *--credentials*=_<credentials>_::
+  Credentials in the format username:password. Example: --credentials 
solr:SolrRocks
+
+*-v*, *--verbose*::
+  Enable verbose mode.
+
+// end::picocli-generated-man-section-options[]
+
+// tag::picocli-generated-man-section-footer[]
+== Examples
+[%hardbreaks]
+  # Assert Solr is running before continuing
+  bin/solr assert --started http://localhost:8983 --timeout 5000
+
+[%hardbreaks]
+  # Assert we are not running as root
+  bin/solr assert --not-root
+
+// end::picocli-generated-man-section-footer[]

Reply via email to