From 091ba2713a5a1aea8987255324e132d817e993c8 Mon Sep 17 00:00:00 2001
From: dgelessus <dgelessus@users.noreply.github.com>
Date: Thu, 27 Feb 2020 12:49:38 +0100
Subject: [PATCH] Improve :version command

Now also displays the version of the ProB 2 Jupyter kernel and more
detailed info about the ProB CLI version.
---
 build.gradle                                  | 15 ++++++++
 notebooks/tests/version.ipynb                 | 12 ++++---
 .../java/de/prob2/jupyter/ProBKernel.java     | 35 +++++++++++++++++++
 .../jupyter/commands/VersionCommand.java      | 18 ++++++++--
 .../de/prob2/jupyter/build.properties         |  4 +++
 5 files changed, 78 insertions(+), 6 deletions(-)
 create mode 100644 src/main/resources/de/prob2/jupyter/build.properties

diff --git a/build.gradle b/build.gradle
index e7527a5..3bf50b7 100644
--- a/build.gradle
+++ b/build.gradle
@@ -52,6 +52,21 @@ tasks.withType(JavaCompile) {
 	options.encoding = SOURCE_ENCODING
 }
 
+def readCurrentGitCommit() {
+	def proc = ["git", "rev-parse", "HEAD"].execute(null, project.projectDir)
+	def exitCode = proc.waitFor()
+	if (exitCode != 0) {
+		throw new IllegalStateException("git rev-parse command exited with status code ${exitCode}:\n" + proc.err.readLines().join("\n"))
+	}
+	return proc.in.readLines()[0]
+}
+
+processResources {
+	filesMatching("de/prob2/jupyter/build.properties") {
+		expand(version: project.version, commit: readCurrentGitCommit())
+	}
+}
+
 mainClassName = "de.prob2.jupyter.Main"
 
 final KERNEL_SPEC_FILES_PATH = sourceSets.main.resources.sourceDirectories.singleFile.toPath().resolve(Paths.get("de", "prob2", "jupyter", "kernelspecfiles"))
diff --git a/notebooks/tests/version.ipynb b/notebooks/tests/version.ipynb
index 9a1b887..b79fdc1 100644
--- a/notebooks/tests/version.ipynb
+++ b/notebooks/tests/version.ipynb
@@ -12,11 +12,11 @@
        ":version\n",
        "```\n",
        "\n",
-       "Display version info about the ProB CLI and ProB 2."
+       "Display version info about the ProB 2 Jupyter kernel, ProB 2, and the underlying ProB CLI."
       ],
       "text/plain": [
        ":version\n",
-       "Display version info about the ProB CLI and ProB 2."
+       "Display version info about the ProB 2 Jupyter kernel, ProB 2, and the underlying ProB CLI."
       ]
      },
      "execution_count": 1,
@@ -36,8 +36,12 @@
     {
      "data": {
       "text/plain": [
-       "ProB CLI: 1.9.3-nightly (50333f1779dcae2a9e6d1b2aa95a23678821f851)\n",
-       "ProB 2: 4.0.0-SNAPSHOT (1d61b6ea7ef1b2e38a6b6045dbb0e81bcb6d8423)"
+       "ProB 2 Jupyter kernel: 1.0.1-SNAPSHOT (2b75217bf8bef7737632efee1ebb1ddbd8cfefe6)\n",
+       "ProB 2: 3.10.0 (0ee5d5eea6894b2899690565dfc3d9042098ce89)\n",
+       "ProB CLI:\n",
+       "\t1.9.3-final (64afef0148d6ce70f2af5082e69269c950078133)\n",
+       "\tLast changed: Wed Feb 19 11:16:13 2020 +0100\n",
+       "\tProlog: SICStus 4.5.1 (x86_64-darwin-17.7.0): Tue Apr  2 15:34:32 CEST 2019"
       ]
      },
      "execution_count": 2,
diff --git a/src/main/java/de/prob2/jupyter/ProBKernel.java b/src/main/java/de/prob2/jupyter/ProBKernel.java
index 8d03ab0..6cbd573 100644
--- a/src/main/java/de/prob2/jupyter/ProBKernel.java
+++ b/src/main/java/de/prob2/jupyter/ProBKernel.java
@@ -1,5 +1,9 @@
 package de.prob2.jupyter;
 
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
@@ -9,6 +13,7 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Properties;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
@@ -70,6 +75,24 @@ import org.slf4j.LoggerFactory;
 
 @Singleton
 public final class ProBKernel extends BaseKernel {
+	/**
+	 * Inner class for safe lazy loading of the build info.
+	 */
+	private static final class BuildInfo {
+		static final Properties buildInfo;
+		static {
+			buildInfo = new Properties();
+			try (final Reader reader = new InputStreamReader(
+				ProBKernel.class.getResourceAsStream("/de/prob2/jupyter/build.properties"),
+				StandardCharsets.UTF_8
+			)) {
+				buildInfo.load(reader);
+			} catch (final IOException e) {
+				throw new AssertionError("Failed to load build info", e);
+			}
+		}
+	}
+	
 	private static final @NotNull Logger LOGGER = LoggerFactory.getLogger(ProBKernel.class);
 	
 	private static final @NotNull Pattern COMMAND_PATTERN = Pattern.compile("\\s*(\\:[^\\s]*)(?:\\h*(.*))?", Pattern.DOTALL);
@@ -196,6 +219,18 @@ public final class ProBKernel extends BaseKernel {
 		this.currentMachineDirectory = Paths.get("");
 	}
 	
+	private static @NotNull Properties getBuildInfo() {
+		return ProBKernel.BuildInfo.buildInfo;
+	}
+	
+	public static @NotNull String getVersion() {
+		return getBuildInfo().getProperty("version");
+	}
+	
+	public static @NotNull String getCommit() {
+		return getBuildInfo().getProperty("commit");
+	}
+	
 	public @NotNull Map<@NotNull String, @NotNull Command> getCommands() {
 		return Collections.unmodifiableMap(this.commands);
 	}
diff --git a/src/main/java/de/prob2/jupyter/commands/VersionCommand.java b/src/main/java/de/prob2/jupyter/commands/VersionCommand.java
index c71915f..b68b59b 100644
--- a/src/main/java/de/prob2/jupyter/commands/VersionCommand.java
+++ b/src/main/java/de/prob2/jupyter/commands/VersionCommand.java
@@ -5,6 +5,7 @@ import com.google.inject.Inject;
 import de.prob.Main;
 import de.prob.animator.command.GetVersionCommand;
 import de.prob.statespace.AnimationSelector;
+import de.prob2.jupyter.ProBKernel;
 
 import io.github.spencerpark.jupyter.kernel.ReplacementOptions;
 import io.github.spencerpark.jupyter.kernel.display.DisplayData;
@@ -34,7 +35,7 @@ public final class VersionCommand implements Command {
 	
 	@Override
 	public @NotNull String getShortHelp() {
-		return "Display version info about the ProB CLI and ProB 2.";
+		return "Display version info about the ProB 2 Jupyter kernel, ProB 2, and the underlying ProB CLI.";
 	}
 	
 	@Override
@@ -44,9 +45,22 @@ public final class VersionCommand implements Command {
 	
 	@Override
 	public @NotNull DisplayData run(final @NotNull String argString) {
+		final StringBuilder sb = new StringBuilder("ProB 2 Jupyter kernel: ");
+		sb.append(ProBKernel.getVersion());
+		sb.append(" (");
+		sb.append(ProBKernel.getCommit());
+		sb.append(")\nProB 2: ");
+		sb.append(Main.getVersion());
+		sb.append(" (");
+		sb.append(Main.getGitSha());
+		sb.append(")\nProB CLI:");
 		final GetVersionCommand cmd = new GetVersionCommand();
 		this.animationSelector.getCurrentTrace().getStateSpace().execute(cmd);
-		return new DisplayData(String.format("ProB CLI: %s\nProB 2: %s (%s)", cmd.getVersion(), Main.getVersion(), Main.getGitSha()));
+		for (final String line : cmd.getVersionString().split("\\n")) {
+			sb.append("\n\t");
+			sb.append(line);
+		}
+		return new DisplayData(sb.toString());
 	}
 	
 	@Override
diff --git a/src/main/resources/de/prob2/jupyter/build.properties b/src/main/resources/de/prob2/jupyter/build.properties
new file mode 100644
index 0000000..9c1de0b
--- /dev/null
+++ b/src/main/resources/de/prob2/jupyter/build.properties
@@ -0,0 +1,4 @@
+# The values for these properties are generated during the build.
+# See the configuration of the processResources task in build.gradle.
+version=${version}
+commit=${commit}
-- 
GitLab