Repository: vmware/tern (python)

Issue:
Add a version argument to tern CLI
**Describe the Feature**
Currently there is no way to identify the version of tern installed.  Typically a CLI will have a `--version` argument to provide such information.

As a added bonus, it's good to put environmental information in the version output that way you can tell people to simply run a command and copy the output into a bug being reported.

**Use Cases**
This would be very helpful for those opening bugs and need to provide version and environment information.

**Implementation Changes**
Describe what changes you think would need to happen to the code to implement this feature
```
pseudocode or python code is fine here
```


Pull request:
Add a version argument to tern CLI
This commit adds a --version option to Tern's CLI. This option will
display the version of Tern that is installed and also output the
python version. This commit also updates the GitHub issue template.
This will be useful for individuals opening bugs to provide the
version of Tern that they are opening the issue against.

Resolves #209

Signed-off-by: Rose Judge <rjudge@vmware.com>

Patch:
diff --git a/.github/ISSUE_TEMPLATE/bug.md b/.github/ISSUE_TEMPLATE/bug.md
index 2d5ea6f..e79f816 100644
--- a/.github/ISSUE_TEMPLATE/bug.md
+++ b/.github/ISSUE_TEMPLATE/bug.md
@@ -24,6 +24,7 @@ A clear and concise description of what you expected to happen.
 
 **Environment you are running Tern on**
 Enter all that apply
+- Output of 'tern --version'
 - Operating System (Linux Distro and version or Mac or Windows)
 - Vagrant file
 - Container OS
diff --git a/tern/__main__.py b/tern/__main__.py
index 6795222..4a9113e 100755
--- a/tern/__main__.py
+++ b/tern/__main__.py
@@ -13,10 +13,13 @@ import argparse
 import importlib
 import logging
 import os
+import sys
 
 from tern.report import report
 from tern.utils import cache
 from tern.utils import constants
+from tern import Version
+
 
 # global logger
 logger = logging.getLogger(constants.logger_name)
@@ -77,6 +80,7 @@ def do_main(args):
 
 def main():
     parser = argparse.ArgumentParser(
+        formatter_class=argparse.RawTextHelpFormatter,
         prog='Tern',
         description='''
            Tern is a container image component curation tool. Tern retrieves
@@ -95,6 +99,11 @@ def main():
                         "Needed when running from within a container")
     parser.add_argument('-r', '--redo', action='store_true',
                         help="Repopulate the cache for found layers")
+    # sys.version gives more information than we care to print
+    py_ver = sys.version.replace('\n', '').split('[')[0]
+    parser.add_argument('-V', '--version', action='version',
+                        version="%(prog)s {0}\n   python version = {1}".format(
+                            Version, py_ver))
     subparsers = parser.add_subparsers(help='Subcommands')
     # subparser for report
     parser_report = subparsers.add_parser('report',
diff --git a/tern/classes/package.py b/tern/classes/package.py
index 1cce92a..a3cc6da 100644
--- a/tern/classes/package.py
+++ b/tern/classes/package.py
@@ -18,6 +18,7 @@ class Package:
         proj_url: package source url
         download_url: package download url
         origins: a list of NoticeOrigin objects
+        checksum: checksum as package property
 
     methods:
         to_dict: returns a dict representation of the instance
@@ -30,6 +31,7 @@ class Package:
         self.__copyright = ''
         self.__proj_url = ''
         self.__download_url = ''
+        self.__checksum = ''
         self.__origins = Origins()
 
     @property
@@ -80,6 +82,14 @@ class Package:
     def origins(self):
         return self.__origins
 
+    @property
+    def checksum(self):
+        return self.__checksum
+
+    @checksum.setter
+    def checksum(self, checksum):
+        self.__checksum = checksum;
+
     def to_dict(self, template=None):
         '''Return a dictionary version of the Package object
         If given an object which is a subclass of Template then map


Test patch:
diff --git a/tests/test_class_package.py b/tests/test_class_package.py
index b491030..314e455 100644
--- a/tests/test_class_package.py
+++ b/tests/test_class_package.py
@@ -19,6 +19,7 @@ class TestClassPackage(unittest.TestCase):
         self.p1.copyright = 'All Rights Reserved'
         self.p1.proj_url = 'github.com'
         self.p1.download_url = 'https://github.com'
+        self.p1.checksum = '123abc456'
 
         self.p2 = Package('p2')
 
@@ -33,6 +34,7 @@ class TestClassPackage(unittest.TestCase):
         self.assertFalse(self.p2.pkg_license)
         self.assertFalse(self.p2.copyright)
         self.assertFalse(self.p2.download_url)
+        self.assertFalse(self.p2.checksum)
 
     def testSetters(self):
         self.assertRaises(AttributeError, setattr, self.p2, 'name', 'y')
@@ -46,6 +48,8 @@ class TestClassPackage(unittest.TestCase):
         self.assertEqual(self.p2.proj_url, 'github.com')
         self.p2.download_url = 'https://github.com'
         self.assertEqual(self.p2.download_url, 'https://github.com')
+        self.p2.checksum = '123abc456'
+        self.assertEqual(self.p2.checksum, '123abc456')
 
     def testGetters(self):
         self.assertEqual(self.p1.name, 'p1')
@@ -54,6 +58,7 @@ class TestClassPackage(unittest.TestCase):
         self.assertEqual(self.p1.copyright, 'All Rights Reserved')
         self.assertEqual(self.p1.proj_url, 'github.com')
         self.assertEqual(self.p1.download_url, 'https://github.com')
+        self.assertEqual(self.p1.checksum, '123abc456')
 
     def testToDict(self):
         a_dict = self.p1.to_dict()
@@ -64,6 +69,7 @@ class TestClassPackage(unittest.TestCase):
         self.assertEqual(a_dict['proj_url'], 'github.com')
         self.assertEqual(a_dict['download_url'], 'https://github.com')
         self.assertFalse(a_dict['origins'])
+        self.assertEqual(a_dict['checksum'], '123abc456')
 
     def testToDictTemplate(self):
         template1 = TestTemplate1()
@@ -82,9 +88,11 @@ class TestClassPackage(unittest.TestCase):
         p.pkg_license = 'Testpkg_license'
         p.version = '1.0'
         p.proj_url = 'TestUrl'
+        p.checksum = 'abcdef'
         self.p2.pkg_license = 'Testpkg_license'
         self.p2.version = '2.0'
         self.p2.proj_url = 'TestUrl'
+        self.p2.checksum = 'abcdef'
         self.assertFalse(self.p2.is_equal(p))
         p.version = '2.0'
         self.assertTrue(self.p2.is_equal(p))
@@ -92,12 +100,14 @@ class TestClassPackage(unittest.TestCase):
     def testFill(self):
         p_dict = {'name': 'p1',
                   'version': '1.0',
-                  'pkg_license': 'Apache 2.0'}
+                  'pkg_license': 'Apache 2.0',
+                  'checksum': 'abcxyz'}
         p = Package('p1')
         p.fill(p_dict)
         self.assertEqual(p.name, 'p1')
         self.assertEqual(p.version, '1.0')
         self.assertEqual(p.pkg_license, 'Apache 2.0')
+        self.assertEqual(p.checksum, 'abcxyz')
         self.assertFalse(p.copyright)
         self.assertFalse(p.proj_url)
         self.assertEqual(len(p.origins.origins), 1)