From 00253b523170d65fae8f97ac714e89010c977700 Mon Sep 17 00:00:00 2001
From: Hunter Mellema <124718352+hpmellema@users.noreply.github.com>
Date: Mon, 31 Jul 2023 10:52:53 -0600
Subject: [PATCH] Add progress tracker and message for CLI while cloning a
template (#1888)
Adds a progress tracker for long running CLI tasks. The progress tracker currently supports a simple repeated dots pattern to indicate execution of a long running task. The progress tracker can be extended in the future to support progress bars and additional loading messages.
---
config/spotbugs/filter.xml | 6 ++
.../smithy/cli/commands/InitCommand.java | 17 +++--
.../smithy/cli/commands/ProgressStyle.java | 58 ++++++++++++++++
.../smithy/cli/commands/ProgressTracker.java | 68 +++++++++++++++++++
4 files changed, 143 insertions(+), 6 deletions(-)
create mode 100644 smithy-cli/src/main/java/software/amazon/smithy/cli/commands/ProgressStyle.java
create mode 100644 smithy-cli/src/main/java/software/amazon/smithy/cli/commands/ProgressTracker.java
diff --git a/config/spotbugs/filter.xml b/config/spotbugs/filter.xml
index 3e4ed00a8e0..65595618241 100644
--- a/config/spotbugs/filter.xml
+++ b/config/spotbugs/filter.xml
@@ -188,4 +188,10 @@
+
+
+
+
+
+
diff --git a/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/InitCommand.java b/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/InitCommand.java
index 8deb92fd084..5b814dba4f4 100644
--- a/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/InitCommand.java
+++ b/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/InitCommand.java
@@ -188,13 +188,18 @@ private void cloneTemplate(Path temp, ObjectNode smithyTemplatesNode, Options op
final String templatePath = getTemplatePath(templateNode, template);
List includedFiles = getIncludedFiles(templateNode);
- // Specify the subdirectory to download
- exec(ListUtils.of("git", "sparse-checkout", "set", "--no-cone", templatePath), temp);
- // add any additional files that should be included
- for (String includedFile : includedFiles) {
- exec(ListUtils.of("git", "sparse-checkout", "add", "--no-cone", includedFile), temp);
+ try (ProgressTracker t = new ProgressTracker(env,
+ ProgressStyle.dots("cloning template", "template cloned"),
+ standardOptions.quiet()
+ )) {
+ // Specify the subdirectory to download
+ exec(ListUtils.of("git", "sparse-checkout", "set", "--no-cone", templatePath), temp);
+ // add any additional files that should be included
+ for (String includedFile : includedFiles) {
+ exec(ListUtils.of("git", "sparse-checkout", "add", "--no-cone", includedFile), temp);
+ }
+ exec(ListUtils.of("git", "checkout"), temp);
}
- exec(ListUtils.of("git", "checkout"), temp);
diff --git a/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/ProgressStyle.java b/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/ProgressStyle.java
new file mode 100644
index 00000000000..176da8f8f11
--- /dev/null
+++ b/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/ProgressStyle.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package software.amazon.smithy.cli.commands;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import software.amazon.smithy.cli.ColorBuffer;
+import software.amazon.smithy.cli.ColorTheme;
+import software.amazon.smithy.cli.Command;
+import software.amazon.smithy.utils.StringUtils;
+
+
+interface ProgressStyle {
+
+ void updateAction(Command.Env env, AtomicInteger tracker);
+
+ void closeAction(Command.Env env);
+
+ static ColorBuffer getBuffer(Command.Env env) {
+ return ColorBuffer.of(env.colors(), env.stdout());
+ }
+
+ static ProgressStyle dots(String progressMessage, String closeMessage) {
+ return new ProgressStyle() {
+ private static final String PROGRESS_CHAR = ".";
+ private static final int TICKER_LENGTH = 3;
+ private final long startTimeMillis = System.currentTimeMillis();
+
+ @Override
+ public void updateAction(Command.Env env, AtomicInteger tracker) {
+ int tickCount = tracker.getAndIncrement();
+ int tickNumber = tickCount % (TICKER_LENGTH + 1);
+ String loadStr = StringUtils.repeat(PROGRESS_CHAR, tickNumber)
+ + StringUtils.repeat(" ", TICKER_LENGTH - tickNumber);
+ try (ColorBuffer buffer = getBuffer(env)) {
+ buffer.print("\r")
+ .print(progressMessage, ColorTheme.NOTE)
+ .print(loadStr, ColorTheme.NOTE);
+ }
+ }
+
+ @Override
+ public void closeAction(Command.Env env) {
+ try (ColorBuffer buffer = getBuffer(env)) {
+ buffer.print("\r")
+ .print(closeMessage, ColorTheme.SUCCESS)
+ .print(" [", ColorTheme.MUTED)
+ .print((System.currentTimeMillis() - startTimeMillis) / 1000.0 + "s",
+ ColorTheme.NOTE)
+ .print("]", ColorTheme.MUTED)
+ .println();
+ }
+ }
+ };
+ }
+}
diff --git a/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/ProgressTracker.java b/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/ProgressTracker.java
new file mode 100644
index 00000000000..d6bcb8f1f98
--- /dev/null
+++ b/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/ProgressTracker.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package software.amazon.smithy.cli.commands;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import software.amazon.smithy.cli.Command;
+
+final class ProgressTracker implements AutoCloseable {
+ private static final ScheduledThreadPoolExecutor EXECUTOR = new ScheduledThreadPoolExecutor(1, runnable -> {
+ Thread thread = Executors.defaultThreadFactory().newThread(runnable);
+ thread.setDaemon(true);
+ return thread;
+ });
+ private static final long INTERVAL_MILLIS = 400L;
+ private final ScheduledFuture> task;
+ private final ProgressStyle style;
+ private final Command.Env env;
+ private final AtomicInteger tracker;
+ private final boolean quiet;
+
+ ProgressTracker(Command.Env env, ProgressStyle style, boolean quiet) {
+ this(env, style, quiet, new AtomicInteger());
+ }
+
+ ProgressTracker(Command.Env env, ProgressStyle style, boolean quiet, AtomicInteger tracker) {
+ this.env = env;
+ this.style = style;
+ this.quiet = quiet;
+ this.tracker = tracker;
+
+ // Do not print a progress bar if the quiet setting is enabled
+ if (quiet) {
+ task = null;
+ } else {
+ task = EXECUTOR.scheduleAtFixedRate(this::write, 0, INTERVAL_MILLIS, TimeUnit.MILLISECONDS);
+ }
+ }
+
+ @Override
+ public void close() {
+ if (!quiet) {
+ task.cancel(false);
+ try {
+ EXECUTOR.schedule(this::executeClose, 0, TimeUnit.NANOSECONDS).get();
+ } catch (ExecutionException | InterruptedException e) { /* ignored */ }
+ }
+ }
+
+ private void write() {
+ style.updateAction(env, tracker);
+ // Flush so the output is written immediately
+ env.stdout().flush();
+ }
+
+ private void executeClose() {
+ style.closeAction(env);
+ // Flush so the output is written immediately
+ env.stdout().flush();
+ }
+}