From 2c95ace39bfaed744e6c054503491015645c6407 Mon Sep 17 00:00:00 2001
From: Lubomír Sedlář <lsedlar@redhat.com>
Date: Jan 22 2019 11:39:25 +0000
Subject: Allow creating latest symlink with full version


JIRA: COMPOSE-3227
Signed-off-by: Lubomír Sedlář <lsedlar@redhat.com>

---

diff --git a/bin/compose-latest-symlink b/bin/compose-latest-symlink
index a0e5856..2512992 100755
--- a/bin/compose-latest-symlink
+++ b/bin/compose-latest-symlink
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
 import argparse
@@ -6,7 +6,7 @@ import os
 import sys
 
 here = sys.path[0]
-if here != '/usr/bin':
+if here != "/usr/bin":
     sys.path.insert(0, os.path.dirname(here))
 
 from compose_utils import symlink
@@ -14,16 +14,29 @@ from compose_utils import symlink
 
 def main(args):
     parser = argparse.ArgumentParser()
-    parser.add_argument('COMPOSE')
-    parser.add_argument('--minor-version', action='store_true')
+    parser.add_argument("COMPOSE")
+    g = parser.add_mutually_exclusive_group()
+    g.add_argument(
+        "--minor-version",
+        dest="version",
+        action="store_const",
+        const=symlink.VersionType.MINOR,
+    )
+    g.add_argument(
+        "--full-version",
+        dest="version",
+        action="store_const",
+        const=symlink.VersionType.FULL,
+    )
 
     args = parser.parse_args()
-    if args.COMPOSE.startswith('http://') or \
-       args.COMPOSE.startswith('https://'):
+    version_type = args.version or symlink.VersionType.MAJOR
+    if args.COMPOSE.startswith("http://") or args.COMPOSE.startswith("https://"):
         # We can't create a symlink to a URL. Just print what it would be.
-        symlink.print_latest_link(args.COMPOSE, args.minor_version)
+        symlink.print_latest_link(args.COMPOSE, version_type=version_type)
     else:
-        symlink.create_latest_link(args.COMPOSE, args.minor_version)
+        symlink.create_latest_link(args.COMPOSE, version_type=version_type)
 
-if __name__ == '__main__':
+
+if __name__ == "__main__":
     main(sys.argv[1:])
diff --git a/compose_utils/symlink.py b/compose_utils/symlink.py
index c64989d..1c4bdf2 100644
--- a/compose_utils/symlink.py
+++ b/compose_utils/symlink.py
@@ -1,9 +1,21 @@
 # -*- coding: utf-8 -*-
 
+"""
+The `minor_version` argument was deprecated in 0.1.27 and should be removed in 0.2.0.
+"""
+
 import os
+import warnings
+
 import productmd
 
 
+class VersionType(object):
+    MAJOR = 0
+    MINOR = 1
+    FULL = 2
+
+
 class ComposeSymlink(object):
     def __init__(self, compose_path):
         self.compose = productmd.compose.Compose(compose_path)
@@ -20,29 +32,61 @@ class ComposeSymlink(object):
         os.symlink(os.path.basename(self.path), temp)
         os.rename(temp, symlink)
 
-    def get_latest_link_name(self, minor_version=False):
+    def get_latest_link_name(self, minor_version=False, version_type=VersionType.MAJOR):
         """Get name of latest- symlink for current compose."""
-        fmt = 'latest-{0.release.short}-{0.release.major_version}'
+        if minor_version is not None:
+            _deprecated_arg("minor_version", "version_type")
         if minor_version:
-            fmt += '.{0.release.minor_version}'
+            version_type = VersionType.MINOR
+
+        formats = {
+            VersionType.MAJOR: "{0.release.major_version}",
+            VersionType.MINOR: "{0.release.major_version}.{0.release.minor_version}",
+            VersionType.FULL: "{0.release.version}",
+        }
+        version = formats[version_type].format(self.compose.info)
+
+        fmt = "latest-{0.release.short}-{1}"
         if self.compose.info.release.is_layered:
             fmt += '-{0.base_product.short}-{0.base_product.version}'
-        return fmt.format(self.compose.info)
+        return fmt.format(self.compose.info, version)
 
-    def print_latest_link(self, minor_version=False):
-        """Print a latest- symlink with optional minor version."""
-        print(self.get_latest_link_name(minor_version=minor_version))
+    def print_latest_link(self, minor_version=None, version_type=VersionType.MAJOR):
+        """Print a latest- symlink with configured version pattern."""
+        if minor_version is not None:
+            _deprecated_arg("minor_version", "version_type")
+        if minor_version:
+            version_type = VersionType.MINOR
+        print(self.get_latest_link_name(version_type=version_type))
 
-    def create_latest_link(self, minor_version=False):
-        """Create a latest- symlink with optional minor version."""
-        self._link(self.get_latest_link_name(minor_version=minor_version))
+    def create_latest_link(self, minor_version=None, version_type=VersionType.MAJOR):
+        """Create a latest- symlink with configured version pattern."""
+        if minor_version is not None:
+            _deprecated_arg("minor_version", "version_type")
+        if minor_version:
+            version_type = VersionType.MINOR
+        self._link(self.get_latest_link_name(version_type=version_type))
 
 
-def print_latest_link(compose_path, minor_version=False):
+def print_latest_link(compose_path, minor_version=None, version_type=VersionType.MAJOR):
     """Convenience function for easy printing of latest symlink."""
-    ComposeSymlink(compose_path).print_latest_link(minor_version)
+    if minor_version is not None:
+        _deprecated_arg("minor_version", "version_type")
+    if minor_version:
+        version_type = VersionType.MINOR
+    ComposeSymlink(compose_path).print_latest_link(version_type=version_type)
 
 
-def create_latest_link(compose_path, minor_version=False):
+def create_latest_link(compose_path, minor_version=None, version_type=VersionType.MAJOR):
     """Convenience function for easy creation of latest symlink."""
-    ComposeSymlink(compose_path).create_latest_link(minor_version)
+    if minor_version is not None:
+        _deprecated_arg("minor_version", "version_type")
+    if minor_version:
+        version_type = VersionType.MINOR
+    ComposeSymlink(compose_path).create_latest_link(version_type=version_type)
+
+
+def _deprecated_arg(arg, replace):
+    warnings.warn(
+        "%r argument is deprecated, use %r instead" % (arg, replace), DeprecationWarning
+    )
diff --git a/doc/compose-latest-symlink.1 b/doc/compose-latest-symlink.1
index 7e7d78e..da081ec 100644
--- a/doc/compose-latest-symlink.1
+++ b/doc/compose-latest-symlink.1
@@ -19,6 +19,9 @@ Print help and exit.
 .TP
 .BR \-\-minor-version
 Include minor version in the symlink name.
+.TP
+.BR \-\-full-version
+Include full version in the symlink name.
 .SH EXIT CODE
 On success, this tools exits with status code of \fB0\fR.
 On failure, the exit code will be \fB1\fR.
diff --git a/tests/test_symlink.py b/tests/test_symlink.py
index f7a8db2..d960c2b 100644
--- a/tests/test_symlink.py
+++ b/tests/test_symlink.py
@@ -3,10 +3,14 @@
 import os
 import shutil
 import tempfile
-import unittest
 import mock
 from six import StringIO
 
+try:
+    import unittest2 as unittest
+except ImportError:
+    import unittest
+
 from compose_utils import symlink
 
 from . import helpers
@@ -49,13 +53,39 @@ class TestLatestSymlink(unittest.TestCase):
 
     def test_with_minor_version(self):
         path = self._write_compose()
-        symlink.create_latest_link(path, minor_version=True)
+        with self.assertWarns(DeprecationWarning):
+            symlink.create_latest_link(path, minor_version=True)
+        self.assertEqual(os.readlink(os.path.join(self.compose_dir, 'latest-DP-1.0')),
+                         'DP-1.0-20160315.t.1')
+
+    def test_with_minor_version_type(self):
+        path = self._write_compose()
+        symlink.create_latest_link(path, version_type=symlink.VersionType.MINOR)
+        self.assertEqual(os.readlink(os.path.join(self.compose_dir, 'latest-DP-1.0')),
+                         'DP-1.0-20160315.t.1')
+
+    def test_with_full_version(self):
+        path = self._write_compose()
+        symlink.create_latest_link(path, version_type=symlink.VersionType.FULL)
         self.assertEqual(os.readlink(os.path.join(self.compose_dir, 'latest-DP-1.0')),
                          'DP-1.0-20160315.t.1')
 
     def test_layered_with_minor_version(self):
         path = self._write_compose(is_layered=True)
-        symlink.create_latest_link(path, minor_version=True)
+        with self.assertWarns(DeprecationWarning):
+            symlink.create_latest_link(path, minor_version=True)
+        self.assertEqual(os.readlink(os.path.join(self.compose_dir, 'latest-DP-1.0-BP-15')),
+                         'DP-1.0-20160315.t.1')
+
+    def test_layered_with_minor_version_type(self):
+        path = self._write_compose(is_layered=True)
+        symlink.create_latest_link(path, version_type=symlink.VersionType.MINOR)
+        self.assertEqual(os.readlink(os.path.join(self.compose_dir, 'latest-DP-1.0-BP-15')),
+                         'DP-1.0-20160315.t.1')
+
+    def test_layered_with_full_version(self):
+        path = self._write_compose(is_layered=True)
+        symlink.create_latest_link(path, version_type=symlink.VersionType.FULL)
         self.assertEqual(os.readlink(os.path.join(self.compose_dir, 'latest-DP-1.0-BP-15')),
                          'DP-1.0-20160315.t.1')
 
@@ -75,6 +105,13 @@ class TestLatestSymlink(unittest.TestCase):
 
     def test_print_symlink_minor_version(self):
         path = self._write_compose()
+        with self.assertWarns(DeprecationWarning):
+            with mock.patch('sys.stdout', new_callable=StringIO) as mock_out:
+                symlink.print_latest_link(path, minor_version=True)
+        self.assertEqual(mock_out.getvalue(), 'latest-DP-1.0\n')
+
+    def test_print_symlink_minor_version_type(self):
+        path = self._write_compose()
         with mock.patch('sys.stdout', new_callable=StringIO) as mock_out:
-            symlink.print_latest_link(path, minor_version=True)
+            symlink.print_latest_link(path, version_type=symlink.VersionType.MINOR)
         self.assertEqual(mock_out.getvalue(), 'latest-DP-1.0\n')

