Repository: provectus/kafka-ui
Issue: Display application version in UI
Labels: type/enhancement
scope/frontend

It would be cool to show which version (git sha or release tag) of the application is launched on UI, this will allow mentioning this info in newly created issues for faster resolving

Issue comments:

Pull request review comments:

Patch that resolved the issue:
diff --git a/kafka-ui-api/pom.xml b/kafka-ui-api/pom.xml
index e4f57e5bd36..a16cb803452 100644
--- a/kafka-ui-api/pom.xml
+++ b/kafka-ui-api/pom.xml
@@ -253,6 +253,29 @@
             <id>prod</id>
             <build>
                 <plugins>
+                    <plugin>
+                        <groupId>pl.project13.maven</groupId>
+                        <artifactId>git-commit-id-plugin</artifactId>
+                        <version>4.0.0</version>
+                        <executions>
+                            <execution>
+                                <id>get-the-git-infos</id>
+                                <goals>
+                                    <goal>revision</goal>
+                                </goals>
+                                <phase>initialize</phase>
+                            </execution>
+                        </executions>
+                        <configuration>
+                            <generateGitPropertiesFile>true</generateGitPropertiesFile>
+                            <generateGitPropertiesFilename>${project.build.outputDirectory}/git.properties</generateGitPropertiesFilename>
+                            <includeOnlyProperties>
+                                <includeOnlyProperty>^git.build.(time|version)$</includeOnlyProperty>
+                                <includeOnlyProperty>^git.commit.id.(abbrev|full)$</includeOnlyProperty>
+                            </includeOnlyProperties>
+                            <commitIdGenerationMode>full</commitIdGenerationMode>
+                        </configuration>
+                    </plugin>
                     <plugin>
                         <artifactId>maven-resources-plugin</artifactId>
                         <executions>
@@ -279,6 +302,10 @@
                         <version>${frontend-maven-plugin.version}</version>
                         <configuration>
                             <workingDirectory>../kafka-ui-react-app</workingDirectory>
+                            <environmentVariables>
+                                <REACT_APP_TAG>v${project.version}</REACT_APP_TAG>
+                                <REACT_APP_COMMIT>${git.commit.id.abbrev}</REACT_APP_COMMIT>
+                            </environmentVariables>
                         </configuration>
                         <executions>
                             <execution>
diff --git a/kafka-ui-react-app/src/components/App.tsx b/kafka-ui-react-app/src/components/App.tsx
index 9daf3dfb8a7..29f81d5828b 100644
--- a/kafka-ui-react-app/src/components/App.tsx
+++ b/kafka-ui-react-app/src/components/App.tsx
@@ -1,15 +1,17 @@
 import './App.scss';
 import React from 'react';
 import { Switch, Route } from 'react-router-dom';
+import { GIT_TAG, GIT_COMMIT } from 'lib/constants';
 import { Alerts } from 'redux/interfaces';
 import NavContainer from './Nav/NavContainer';
 import PageLoader from './common/PageLoader/PageLoader';
 import Dashboard from './Dashboard/Dashboard';
 import Cluster from './Cluster/Cluster';
+import Version from './Version/Version';
 import Alert from './Alert/Alert';
 
 export interface AppProps {
-  isClusterListFetched: boolean;
+  isClusterListFetched?: boolean;
   alerts: Alerts;
   fetchClustersList: () => void;
 }
@@ -35,6 +37,11 @@ const App: React.FC<AppProps> = ({
             Kafka UI
           </a>
         </div>
+        <div className="navbar-end">
+          <div className="navbar-item mr-2">
+            <Version tag={GIT_TAG} commit={GIT_COMMIT} />
+          </div>
+        </div>
       </nav>
 
       <main className="Layout__container">
diff --git a/kafka-ui-react-app/src/components/Version/Version.tsx b/kafka-ui-react-app/src/components/Version/Version.tsx
new file mode 100644
index 00000000000..954679ba279
--- /dev/null
+++ b/kafka-ui-react-app/src/components/Version/Version.tsx
@@ -0,0 +1,35 @@
+import React from 'react';
+import { GIT_REPO_LINK } from 'lib/constants';
+
+export interface VesionProps {
+  tag?: string;
+  commit?: string;
+}
+
+const Version: React.FC<VesionProps> = ({ tag, commit }) => {
+  if (!tag) {
+    return null;
+  }
+
+  return (
+    <div className="is-size-7 has-text-grey">
+      <span className="has-text-grey-light mr-1">Version:</span>
+      <span className="mr-1">{tag}</span>
+      {commit && (
+        <>
+          <span>&#40;</span>
+          <a
+            title="Current commit"
+            target="__blank"
+            href={`${GIT_REPO_LINK}/commit/${commit}`}
+          >
+            {commit}
+          </a>
+          <span>&#41;</span>
+        </>
+      )}
+    </div>
+  );
+};
+
+export default Version;
diff --git a/kafka-ui-react-app/src/components/Version/__tests__/Version.spec.tsx b/kafka-ui-react-app/src/components/Version/__tests__/Version.spec.tsx
new file mode 100644
index 00000000000..84e4c011f5d
--- /dev/null
+++ b/kafka-ui-react-app/src/components/Version/__tests__/Version.spec.tsx
@@ -0,0 +1,29 @@
+import React from 'react';
+import { mount } from 'enzyme';
+import Version from '../Version';
+
+const tag = 'v1.0.1-SHAPSHOT';
+const commit = '123sdf34';
+
+describe('Version', () => {
+  it('shows nothing if tag is not defined', () => {
+    const component = mount(<Version />);
+    expect(component.html()).toEqual(null);
+  });
+
+  it('shows current tag when only tag is defined', () => {
+    const component = mount(<Version tag={tag} />);
+    expect(component.text()).toContain(tag);
+  });
+
+  it('shows current tag and commit', () => {
+    const component = mount(<Version tag={tag} commit={commit} />);
+    expect(component.text()).toContain(tag);
+    expect(component.text()).toContain(commit);
+  });
+
+  it('matches snapshot', () => {
+    const component = mount(<Version tag={tag} commit={commit} />);
+    expect(component).toMatchSnapshot();
+  });
+});
diff --git a/kafka-ui-react-app/src/components/Version/__tests__/__snapshots__/Version.spec.tsx.snap b/kafka-ui-react-app/src/components/Version/__tests__/__snapshots__/Version.spec.tsx.snap
new file mode 100644
index 00000000000..6b3eb009dd0
--- /dev/null
+++ b/kafka-ui-react-app/src/components/Version/__tests__/__snapshots__/Version.spec.tsx.snap
@@ -0,0 +1,36 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Version matches snapshot 1`] = `
+<Version
+  commit="123sdf34"
+  tag="v1.0.1-SHAPSHOT"
+>
+  <div
+    className="is-size-7 has-text-grey"
+  >
+    <span
+      className="has-text-grey-light mr-1"
+    >
+      Version:
+    </span>
+    <span
+      className="mr-1"
+    >
+      v1.0.1-SHAPSHOT
+    </span>
+    <span>
+      (
+    </span>
+    <a
+      href="https://github.com/provectus/kafka-ui/commit/123sdf34"
+      target="__blank"
+      title="Current commit"
+    >
+      123sdf34
+    </a>
+    <span>
+      )
+    </span>
+  </div>
+</Version>
+`;
diff --git a/kafka-ui-react-app/src/components/__test__/App.spec.tsx b/kafka-ui-react-app/src/components/__tests__/App.spec.tsx
similarity index 64%
rename from kafka-ui-react-app/src/components/__test__/App.spec.tsx
rename to kafka-ui-react-app/src/components/__tests__/App.spec.tsx
index 7118f1c9990..f37dc02f111 100644
--- a/kafka-ui-react-app/src/components/__test__/App.spec.tsx
+++ b/kafka-ui-react-app/src/components/__tests__/App.spec.tsx
@@ -1,7 +1,7 @@
 import React from 'react';
 import { mount, shallow } from 'enzyme';
 import { Provider } from 'react-redux';
-import { StaticRouter } from 'react-router';
+import { StaticRouter } from 'react-router-dom';
 import configureStore from 'redux/store/configureStore';
 import App, { AppProps } from '../App';
 
@@ -18,12 +18,7 @@ describe('App', () => {
     />
   );
 
-  it('matches snapshot with initial props', () => {
-    const wrapper = shallow(setupComponent());
-    expect(wrapper).toMatchSnapshot();
-  });
-
-  it('correctly mounts App component', () => {
+  it('handles fetchClustersList', () => {
     const wrapper = mount(
       <Provider store={store}>
         <StaticRouter>{setupComponent()}</StaticRouter>
@@ -33,12 +28,13 @@ describe('App', () => {
     expect(fetchClustersList).toHaveBeenCalledTimes(1);
   });
 
-  it('correctly renders PageLoader', () => {
-    const wrapper = shallow(setupComponent({ isClusterListFetched: false }));
-    expect(wrapper.exists('PageLoader')).toBeTruthy();
-
-    wrapper.setProps({ isClusterListFetched: true });
-    expect(wrapper.exists('PageLoader')).toBeFalsy();
+  it('shows PageLoader until cluster list is fetched', () => {
+    const component = shallow(setupComponent({ isClusterListFetched: false }));
+    expect(component.exists('.Layout__container PageLoader')).toBeTruthy();
+    expect(component.exists('.Layout__container Switch')).toBeFalsy();
+    component.setProps({ isClusterListFetched: true });
+    expect(component.exists('.Layout__container PageLoader')).toBeFalsy();
+    expect(component.exists('.Layout__container Switch')).toBeTruthy();
   });
 
   it('correctly renders alerts', () => {
@@ -57,4 +53,9 @@ describe('App', () => {
     expect(wrapper.exists('Alert')).toBeTruthy();
     expect(wrapper.find('Alert').length).toEqual(1);
   });
+
+  it('matches snapshot', () => {
+    const component = shallow(setupComponent());
+    expect(component).toMatchSnapshot();
+  });
 });
diff --git a/kafka-ui-react-app/src/components/__test__/__snapshots__/App.spec.tsx.snap b/kafka-ui-react-app/src/components/__tests__/__snapshots__/App.spec.tsx.snap
similarity index 82%
rename from kafka-ui-react-app/src/components/__test__/__snapshots__/App.spec.tsx.snap
rename to kafka-ui-react-app/src/components/__tests__/__snapshots__/App.spec.tsx.snap
index 48041898329..c6531a057f5 100644
--- a/kafka-ui-react-app/src/components/__test__/__snapshots__/App.spec.tsx.snap
+++ b/kafka-ui-react-app/src/components/__tests__/__snapshots__/App.spec.tsx.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`App matches snapshot with initial props 1`] = `
+exports[`App matches snapshot 1`] = `
 <div
   className="Layout"
 >
@@ -19,6 +19,15 @@ exports[`App matches snapshot with initial props 1`] = `
         Kafka UI
       </a>
     </div>
+    <div
+      className="navbar-end"
+    >
+      <div
+        className="navbar-item mr-2"
+      >
+        <Version />
+      </div>
+    </div>
   </nav>
   <main
     className="Layout__container"
diff --git a/kafka-ui-react-app/src/lib/constants.ts b/kafka-ui-react-app/src/lib/constants.ts
index 76de060f45f..f49da1ba0da 100644
--- a/kafka-ui-react-app/src/lib/constants.ts
+++ b/kafka-ui-react-app/src/lib/constants.ts
@@ -43,3 +43,7 @@ export const MILLISECONDS_IN_SECOND = 1_000;
 export const BYTES_IN_GB = 1_073_741_824;
 
 export const PER_PAGE = 25;
+
+export const GIT_REPO_LINK = 'https://github.com/provectus/kafka-ui';
+export const GIT_TAG = process.env.REACT_APP_TAG;
+export const GIT_COMMIT = process.env.REACT_APP_COMMIT;


Test patch: