6
0
mirror of https://github.com/ChristianLight/tutor.git synced 2024-12-12 22:27:47 +00:00

code: refactor version checking code

This commit is contained in:
Régis Behmo 2022-01-08 12:19:46 +01:00 committed by Régis Behmo
parent c61accedfc
commit 1daba42f1e
2 changed files with 89 additions and 42 deletions

View File

@ -3,9 +3,11 @@ import tempfile
import unittest import unittest
from unittest.mock import Mock, patch from unittest.mock import Mock, patch
from tutor.__about__ import __version__
from tutor import config as tutor_config from tutor import config as tutor_config
from tutor import env, exceptions, fmt from tutor import env, exceptions, fmt
from tutor.types import Config from tutor.types import Config
from tests.helpers import temporary_root
class EnvTests(unittest.TestCase): class EnvTests(unittest.TestCase):
@ -30,9 +32,9 @@ class EnvTests(unittest.TestCase):
self.assertTrue(os.path.exists(path)) self.assertTrue(os.path.exists(path))
def test_pathjoin(self) -> None: def test_pathjoin(self) -> None:
with tempfile.TemporaryDirectory() as root: with temporary_root() as root:
self.assertEqual( self.assertEqual(
os.path.join(root, "env", "dummy"), env.pathjoin(root, "dummy") os.path.join(env.base_dir(root), "dummy"), env.pathjoin(root, "dummy")
) )
def test_render_str(self) -> None: def test_render_str(self) -> None:
@ -76,21 +78,26 @@ class EnvTests(unittest.TestCase):
) )
def test_save_full(self) -> None: def test_save_full(self) -> None:
with tempfile.TemporaryDirectory() as root: with temporary_root() as root:
config = tutor_config.load_full(root) config = tutor_config.load_full(root)
with patch.object(fmt, "STDOUT"): with patch.object(fmt, "STDOUT"):
env.save(root, config) env.save(root, config)
self.assertTrue( self.assertTrue(
os.path.exists(os.path.join(root, "env", "local", "docker-compose.yml")) os.path.exists(
os.path.join(env.base_dir(root), "local", "docker-compose.yml")
)
) )
def test_save_full_with_https(self) -> None: def test_save_full_with_https(self) -> None:
with tempfile.TemporaryDirectory() as root: with temporary_root() as root:
config = tutor_config.load_full(root) config = tutor_config.load_full(root)
config["ENABLE_HTTPS"] = True config["ENABLE_HTTPS"] = True
with patch.object(fmt, "STDOUT"): with patch.object(fmt, "STDOUT"):
env.save(root, config) env.save(root, config)
with open(os.path.join(root, "env", "apps", "caddy", "Caddyfile")) as f: with open(
os.path.join(env.base_dir(root), "apps", "caddy", "Caddyfile"),
encoding="utf-8",
) as f:
self.assertIn("www.myopenedx.com{$default_site_port}", f.read()) self.assertIn("www.myopenedx.com{$default_site_port}", f.read())
def test_patch(self) -> None: def test_patch(self) -> None:
@ -120,11 +127,15 @@ class EnvTests(unittest.TestCase):
# Create two templates # Create two templates
os.makedirs(os.path.join(plugin_templates, "plugin1", "apps")) os.makedirs(os.path.join(plugin_templates, "plugin1", "apps"))
with open( with open(
os.path.join(plugin_templates, "plugin1", "unrendered.txt"), "w" os.path.join(plugin_templates, "plugin1", "unrendered.txt"),
"w",
encoding="utf-8",
) as f: ) as f:
f.write("This file should not be rendered") f.write("This file should not be rendered")
with open( with open(
os.path.join(plugin_templates, "plugin1", "apps", "rendered.txt"), "w" os.path.join(plugin_templates, "plugin1", "apps", "rendered.txt"),
"w",
encoding="utf-8",
) as f: ) as f:
f.write("Hello my ID is {{ ID }}") f.write("Hello my ID is {{ ID }}")
@ -137,7 +148,7 @@ class EnvTests(unittest.TestCase):
"iter_enabled", "iter_enabled",
return_value=[plugin1], return_value=[plugin1],
): ):
with tempfile.TemporaryDirectory() as root: with temporary_root() as root:
# Render plugin templates # Render plugin templates
env.save_plugin_templates(plugin1, root, config) env.save_plugin_templates(plugin1, root, config)
@ -150,7 +161,7 @@ class EnvTests(unittest.TestCase):
) )
self.assertFalse(os.path.exists(dst_unrendered)) self.assertFalse(os.path.exists(dst_unrendered))
self.assertTrue(os.path.exists(dst_rendered)) self.assertTrue(os.path.exists(dst_rendered))
with open(dst_rendered) as f: with open(dst_rendered, encoding="utf-8") as f:
self.assertEqual("Hello my ID is abcd", f.read()) self.assertEqual("Hello my ID is abcd", f.read())
def test_renderer_is_reset_on_config_change(self) -> None: def test_renderer_is_reset_on_config_change(self) -> None:
@ -161,7 +172,9 @@ class EnvTests(unittest.TestCase):
# Create one template # Create one template
os.makedirs(os.path.join(plugin_templates, plugin1.name)) os.makedirs(os.path.join(plugin_templates, plugin1.name))
with open( with open(
os.path.join(plugin_templates, plugin1.name, "myplugin.txt"), "w" os.path.join(plugin_templates, plugin1.name, "myplugin.txt"),
"w",
encoding="utf-8",
) as f: ) as f:
f.write("some content") f.write("some content")
@ -199,3 +212,38 @@ class EnvTests(unittest.TestCase):
) )
), ),
) )
def test_current_version_in_empty_env(self) -> None:
with temporary_root() as root:
self.assertIsNone(env.current_version(root))
self.assertIsNone(env.current_release(root))
self.assertFalse(env.needs_major_upgrade(root))
self.assertTrue(env.is_up_to_date(root))
def test_current_version_in_lilac_env(self) -> None:
with temporary_root() as root:
os.makedirs(env.base_dir(root))
with open(
os.path.join(env.base_dir(root), env.VERSION_FILENAME),
"w",
encoding="utf-8",
) as f:
f.write("12.0.46")
self.assertEqual("12.0.46", env.current_version(root))
self.assertEqual("lilac", env.current_release(root))
self.assertTrue(env.needs_major_upgrade(root))
self.assertFalse(env.is_up_to_date(root))
def test_current_version_in_latest_env(self) -> None:
with temporary_root() as root:
os.makedirs(env.base_dir(root))
with open(
os.path.join(env.base_dir(root), env.VERSION_FILENAME),
"w",
encoding="utf-8",
) as f:
f.write(__version__)
self.assertEqual(__version__, env.current_version(root))
self.assertEqual("maple", env.current_release(root))
self.assertFalse(env.needs_major_upgrade(root))
self.assertTrue(env.is_up_to_date(root))

View File

@ -1,4 +1,3 @@
import codecs
import os import os
from copy import deepcopy from copy import deepcopy
from typing import Any, Iterable, List, Optional, Type, Union from typing import Any, Iterable, List, Optional, Type, Union
@ -139,9 +138,7 @@ class Renderer:
try: try:
patches.append(self.render_str(patch)) patches.append(self.render_str(patch))
except exceptions.TutorError: except exceptions.TutorError:
fmt.echo_error( fmt.echo_error(f"Error rendering patch '{name}' from plugin {plugin}")
"Error rendering patch '{}' from plugin {}".format(name, plugin)
)
raise raise
rendered = separator.join(patches) rendered = separator.join(patches)
if rendered: if rendered:
@ -193,9 +190,7 @@ class Renderer:
try: try:
return template.render(**self.config) return template.render(**self.config)
except jinja2.exceptions.UndefinedError as e: except jinja2.exceptions.UndefinedError as e:
raise exceptions.TutorError( raise exceptions.TutorError(f"Missing configuration value: {e.args[0]}")
"Missing configuration value: {}".format(e.args[0])
)
def save(root: str, config: Config) -> None: def save(root: str, config: Config) -> None:
@ -219,7 +214,7 @@ def save(root: str, config: Config) -> None:
save_plugin_templates(plugin, root, config) save_plugin_templates(plugin, root, config)
upgrade_obsolete(root) upgrade_obsolete(root)
fmt.echo_info("Environment generated in {}".format(base_dir(root))) fmt.echo_info(f"Environment generated in {base_dir(root)}")
def upgrade_obsolete(_root: str) -> None: def upgrade_obsolete(_root: str) -> None:
@ -280,7 +275,7 @@ def render_unknown(config: Config, value: Any) -> Any:
""" """
if isinstance(value, str): if isinstance(value, str):
return render_str(config, value) return render_str(config, value)
elif isinstance(value, dict): if isinstance(value, dict):
return {k: render_unknown(config, v) for k, v in value.items()} return {k: render_unknown(config, v) for k, v in value.items()}
return value return value
@ -299,15 +294,12 @@ def render_str(config: Config, text: str) -> str:
def check_is_up_to_date(root: str) -> None: def check_is_up_to_date(root: str) -> None:
if not is_up_to_date(root): if not is_up_to_date(root):
message = (
"The current environment stored at {} is not up-to-date: it is at "
"v{} while the 'tutor' binary is at v{}. You should upgrade "
"the environment by running:\n"
"\n"
" tutor config save"
)
fmt.echo_alert( fmt.echo_alert(
message.format(base_dir(root), current_version(root), __version__) f"The current environment stored at {base_dir(root)} is not up-to-date: it is at "
f"v{current_version(root)} while the 'tutor' binary is at v{__version__}. You should upgrade "
f"the environment by running:\n"
f"\n"
f" tutor config save"
) )
@ -315,22 +307,29 @@ def is_up_to_date(root: str) -> bool:
""" """
Check if the currently rendered version is equal to the current tutor version. Check if the currently rendered version is equal to the current tutor version.
""" """
return current_version(root) == __version__ current = current_version(root)
return current is None or current == __version__
def needs_major_upgrade(root: str) -> bool: def needs_major_upgrade(root: str) -> bool:
""" """
Return the current version as a tuple of int. E.g: (1, 0, 2). Return true if the current version is less than the tutor version.
""" """
current = int(current_version(root).split(".")[0]) current = current_version(root)
required = int(__version__.split(".")[0]) if current is None:
return 0 < current < required return False
current_as_int = int(current.split(".")[0])
required = int(__version__.split(".", maxsplit=1)[0])
return 0 < current_as_int < required
def current_release(root: str) -> str: def current_release(root: str) -> Optional[str]:
""" """
Return the name of the current Open edX release. Return the name of the current Open edX release. If the current environment has no version, return None.
""" """
current = current_version(root)
if current is None:
return None
return { return {
"0": "ironwood", "0": "ironwood",
"3": "ironwood", "3": "ironwood",
@ -338,19 +337,19 @@ def current_release(root: str) -> str:
"11": "koa", "11": "koa",
"12": "lilac", "12": "lilac",
"13": "maple", "13": "maple",
}[current_version(root).split(".")[0]] }[current.split(".")[0]]
def current_version(root: str) -> str: def current_version(root: str) -> Optional[str]:
""" """
Return the current environment version. If the current environment has no version, Return the current environment version. If the current environment has no version,
return "0.0.0". return None.
""" """
path = pathjoin(root, VERSION_FILENAME) path = pathjoin(root, VERSION_FILENAME)
if not os.path.exists(path): if not os.path.exists(path):
return "0.0.0" return None
with open(path) as f: with open(path, encoding="utf-8") as fi:
return f.read().strip() return fi.read().strip()
def read_template_file(*path: str) -> str: def read_template_file(*path: str) -> str:
@ -358,7 +357,7 @@ def read_template_file(*path: str) -> str:
Read raw content of template located at `path`. Read raw content of template located at `path`.
""" """
src = template_path(*path) src = template_path(*path)
with codecs.open(src, encoding="utf-8") as fi: with open(src, encoding="utf-8") as fi:
return fi.read() return fi.read()