6
0
mirror of https://github.com/ChristianLight/tutor.git synced 2025-02-12 06:08:26 +00:00

Add convenient fmt.echo_* functions

This commit is contained in:
Régis Behmo 2019-05-12 00:10:14 +02:00
parent 407659ff06
commit 0199a1e916
14 changed files with 92 additions and 97 deletions

View File

@ -20,8 +20,9 @@ class ConfigTests(unittest.TestCase):
tutor_config.merge(config, defaults)
self.assertEqual("abcd", config["MYSQL_ROOT_PASSWORD"])
def test_save_twice(self):
@unittest.mock.patch.object(tutor_config.fmt, "echo")
def test_save_twice(self, mock_echo):
with tempfile.TemporaryDirectory() as root:
tutor_config.save(root, silent=True)
config1 = tutor_config.load_user(root)
@ -31,7 +32,8 @@ class ConfigTests(unittest.TestCase):
self.assertEqual(config1, config2)
def test_removed_entry_is_added_on_save(self):
@unittest.mock.patch.object(tutor_config.fmt, "echo")
def test_removed_entry_is_added_on_save(self, mock_echo):
with tempfile.TemporaryDirectory() as root:
with unittest.mock.patch.object(
tutor_config.utils, "random_string"

View File

@ -1,5 +1,6 @@
import tempfile
import unittest
import unittest.mock
from tutor.commands import config as tutor_config
from tutor import env
@ -7,6 +8,7 @@ from tutor import exceptions
class EnvTests(unittest.TestCase):
def test_walk_templates(self):
templates = list(env.walk_templates("local"))
self.assertIn("local/docker-compose.yml", templates)
@ -32,7 +34,8 @@ class EnvTests(unittest.TestCase):
rendered = env.render_file(config, "scripts", "create_databases.sh")
self.assertIn("testpassword", rendered)
def test_render_file_missing_configuration(self):
@unittest.mock.patch.object(tutor_config.fmt, "echo")
def test_render_file_missing_configuration(self, _):
self.assertRaises(
exceptions.TutorError, env.render_file, {}, "local", "docker-compose.yml"
)

View File

@ -21,11 +21,9 @@ def build():
@opts.root
def debug(root):
docker_run(root)
click.echo(
fmt.info(
"The debuggable APK file is available in {}".format(
tutor_env.data_path(root, "android")
)
fmt.echo_info(
"The debuggable APK file is available in {}".format(
tutor_env.data_path(root, "android")
)
)
@ -34,11 +32,9 @@ def debug(root):
@opts.root
def release(root):
docker_run(root, "./gradlew", "assembleProdRelease")
click.echo(
fmt.info(
"The production APK file is available in {}".format(
tutor_env.data_path(root, "android")
)
fmt.echo_info(
"The production APK file is available in {}".format(
tutor_env.data_path(root, "android")
)
)

View File

@ -21,7 +21,7 @@ def main():
try:
cli()
except exceptions.TutorError as e:
sys.stderr.write(fmt.error("Error: {}\n".format(e.args[0])))
fmt.echo_error("Error: {}".format(e.args[0]))
sys.exit(1)

View File

@ -58,7 +58,7 @@ def printvalue(root, key):
config = load_current(root, defaults)
merge(config, defaults)
try:
print(config[key])
fmt.echo(config[key])
except KeyError:
raise exceptions.TutorError("Missing configuration value: {}".format(key))
@ -84,20 +84,31 @@ def load(root):
if should_update_env:
save_env(root, config)
merge(config, defaults)
return config
def merge(config, defaults):
"""
Merge default values with user configuration.
"""
for key, value in defaults.items():
if key not in config:
if isinstance(value, str):
config[key] = env.render_str(config, value)
else:
config[key] = value
def pre_upgrade_announcement(root):
"""
Inform the user that the current environment is not up-to-date. Crash if running in
non-interactive mode.
"""
click.echo(
fmt.alert(
"The current environment stored at {} is not up-to-date: it is at "
"v{} while the 'tutor' binary is at v{}.".format(
env.base_dir(root), env.version(root), __version__
)
fmt.echo_alert(
"The current environment stored at {} is not up-to-date: it is at "
"v{} while the 'tutor' binary is at v{}.".format(
env.base_dir(root), env.version(root), __version__
)
)
if os.isatty(sys.stdin.fileno()):
@ -339,10 +350,8 @@ def convert_json2yml(root):
config = json.load(fi)
save_config(root, config)
os.remove(json_path)
click.echo(
fmt.info(
"File config.json detected in {} and converted to config.yml".format(root)
)
fmt.echo_info(
"File config.json detected in {} and converted to config.yml".format(root)
)
@ -351,12 +360,12 @@ def save_config(root, config):
utils.ensure_file_directory_exists(path)
with open(path, "w") as of:
serialize.dump(config, of)
click.echo(fmt.info("Configuration saved to {}".format(path)))
fmt.echo_info("Configuration saved to {}".format(path))
def save_env(root, config):
env.render_full(root, config)
click.echo(fmt.info("Environment generated in {}".format(env.base_dir(root))))
fmt.echo_info("Environment generated in {}".format(env.base_dir(root)))
def config_path(root):

View File

@ -49,7 +49,7 @@ def build(root, image, no_cache, build_arg):
config = tutor_config.load(root)
for img in openedx_image_names(config, image):
tag = get_tag(config, img)
click.echo(fmt.info("Building image {}".format(tag)))
fmt.echo_info("Building image {}".format(tag))
command = ["build", "-t", tag, tutor_env.pathjoin(root, "build", img)]
if no_cache:
command.append("--no-cache")
@ -65,7 +65,7 @@ def pull(root, image):
config = tutor_config.load(root)
for img in image_names(config, image):
tag = get_tag(config, img)
click.echo(fmt.info("Pulling image {}".format(tag)))
fmt.echo_info("Pulling image {}".format(tag))
utils.execute("docker", "pull", tag)
@ -75,7 +75,7 @@ def pull(root, image):
def push(root, image):
config = tutor_config.load(root)
for tag in openedx_image_tags(config, image):
click.echo(fmt.info("Pushing image {}".format(tag)))
fmt.echo_info("Pushing image {}".format(tag))
utils.execute("docker", "push", tag)

View File

@ -116,9 +116,9 @@ def createuser(root, superuser, staff, name, email):
@click.command(help="Import the demo course")
@opts.root
def importdemocourse(root):
click.echo(fmt.info("Importing demo course"))
fmt.echo_info("Importing demo course")
scripts.import_demo_course(root, run_sh)
click.echo(fmt.info("Re-indexing courses"))
fmt.echo_info("Re-indexing courses")
indexcourses.callback(root)

View File

@ -59,7 +59,7 @@ def start(root, detach):
docker_compose(root, config, *command)
if detach:
click.echo(fmt.info("The Open edX platform is now running in detached mode"))
fmt.echo_info("The Open edX platform is now running in detached mode")
http = "https" if config["ACTIVATE_HTTPS"] else "http"
urls = []
if not config["ACTIVATE_HTTPS"] and not config["WEB_PROXY"]:
@ -70,13 +70,11 @@ def start(root, detach):
urls.append(
"{http}://{cms_host}".format(http=http, cms_host=config["CMS_HOST"])
)
click.echo(
fmt.info(
"""Your Open edX platform is ready and can be accessed at the following urls:
fmt.echo_info(
"""Your Open edX platform is ready and can be accessed at the following urls:
{}""".format(
"\n ".join(urls)
)
"\n ".join(urls)
)
)
@ -166,23 +164,21 @@ def https_create(root):
"""
config = tutor_config.load(root)
if not config["ACTIVATE_HTTPS"]:
click.echo(fmt.info("HTTPS is not activated: certificate generation skipped"))
fmt.echo_info("HTTPS is not activated: certificate generation skipped")
return
script = scripts.render_template(config, "https_create.sh")
if config["WEB_PROXY"]:
click.echo(
fmt.info(
"""You are running Tutor behind a web proxy (WEB_PROXY=true): SSL/TLS
fmt.echo_info(
"""You are running Tutor behind a web proxy (WEB_PROXY=true): SSL/TLS
certificates must be generated on the host. For instance, to generate
certificates with Let's Encrypt, run:
{}
See the official certbot documentation for your platform: https://certbot.eff.org/""".format(
indent(script, " ")
)
indent(script, " ")
)
)
return
@ -205,19 +201,17 @@ See the official certbot documentation for your platform: https://certbot.eff.or
def https_renew(root):
config = tutor_config.load(root)
if not config["ACTIVATE_HTTPS"]:
click.echo(fmt.info("HTTPS is not activated: certificate renewal skipped"))
fmt.echo_info("HTTPS is not activated: certificate renewal skipped")
return
if config["WEB_PROXY"]:
click.echo(
fmt.info(
"""You are running Tutor behind a web proxy (WEB_PROXY=true): SSL/TLS
fmt.echo_info(
"""You are running Tutor behind a web proxy (WEB_PROXY=true): SSL/TLS
certificates must be renewed on the host. For instance, to renew Let's Encrypt
certificates, run:
certbot renew
See the official certbot documentation for your platform: https://certbot.eff.org/"""
)
)
return
docker_run = [
@ -264,9 +258,9 @@ def createuser(root, superuser, staff, name, email):
def importdemocourse(root):
config = tutor_config.load(root)
check_service_is_activated(config, "cms")
click.echo(fmt.info("Importing demo course"))
fmt.echo_info("Importing demo course")
scripts.import_demo_course(root, run_sh)
click.echo(fmt.info("Re-indexing courses"))
fmt.echo_info("Re-indexing courses")
indexcourses.callback(root)
@ -295,9 +289,7 @@ def portainer(root, port):
"portainer/portainer:latest",
"--bind=:{}".format(port),
]
click.echo(
fmt.info("View the Portainer UI at http://localhost:{port}".format(port=port))
)
fmt.echo_info("View the Portainer UI at http://localhost:{port}".format(port=port))
utils.docker_run(*docker_run)

View File

@ -38,7 +38,7 @@ def webui():
)
def start(root, port, host):
check_gotty_binary(root)
click.echo(fmt.info("Access the Tutor web UI at http://{}:{}".format(host, port)))
fmt.echo_info("Access the Tutor web UI at http://{}:{}".format(host, port))
while True:
config = load_config(root)
user = config["user"]
@ -57,10 +57,8 @@ def start(root, port, host):
credential = "{}:{}".format(user, password)
command += ["--credential", credential]
else:
click.echo(
fmt.alert(
"Running web UI without user authentication. Run 'tutor webui configure' to setup authentication"
)
fmt.echo_alert(
"Running web UI without user authentication. Run 'tutor webui configure' to setup authentication"
)
command += [sys.argv[0], "ui"]
p = subprocess.Popen(command)
@ -91,12 +89,10 @@ def start(root, port, host):
)
def configure(root, user, password):
save_config(root, {"user": user, "password": password})
click.echo(
fmt.info(
"The web UI configuration has been updated. "
"If at any point you wish to reset your username and password, "
"just delete the following file:\n\n {}".format(config_path(root))
)
fmt.echo_info(
"The web UI configuration has been updated. "
"If at any point you wish to reset your username and password, "
"just delete the following file:\n\n {}".format(config_path(root))
)
@ -104,7 +100,7 @@ def check_gotty_binary(root):
path = gotty_path(root)
if os.path.exists(path):
return
click.echo(fmt.info("Downloading gotty to {}...".format(path)))
fmt.echo_info("Downloading gotty to {}...".format(path))
# Generate release url
# Note: I don't know how to handle arm

View File

@ -5,6 +5,7 @@ import shutil
import jinja2
from . import exceptions
from . import fmt
from . import utils
from .__about__ import __version__
@ -45,10 +46,10 @@ class Renderer:
try:
return cls.__render(template, config)
except (jinja2.exceptions.TemplateError, exceptions.TutorError):
print("Error rendering template", path)
fmt.echo_error("Error rendering template " + path)
raise
except Exception:
print("Unknown error rendering template", path)
fmt.echo_error("Unknown error rendering template " + path)
raise
@classmethod

View File

@ -10,6 +10,10 @@ def title(text):
return click.style(message, fg="green")
def echo_info(text):
echo(info(text))
def info(text):
return click.style(text, fg="blue")
@ -18,6 +22,10 @@ def error(text):
return click.style(text, fg="red")
def echo_error(text):
echo(error(text), err=True)
def command(text):
return click.style(text, fg="magenta")
@ -26,5 +34,13 @@ def question(text):
return click.style(text, fg="yellow")
def echo_alert(text):
echo(alert(text))
def alert(text):
return click.style("⚠️ " + text, fg="yellow", bold=True)
def echo(text, err=False):
click.echo(text, err=err)

View File

@ -5,28 +5,28 @@ from . import fmt
def migrate(root, config, run_func):
click.echo(fmt.info("Creating all databases..."))
fmt.echo_info("Creating all databases...")
run_script(root, config, "mysql-client", "create_databases.sh", run_func)
if config["ACTIVATE_LMS"]:
click.echo(fmt.info("Running lms migrations..."))
fmt.echo_info("Running lms migrations...")
run_script(root, config, "lms", "migrate_lms.sh", run_func)
if config["ACTIVATE_CMS"]:
click.echo(fmt.info("Running cms migrations..."))
fmt.echo_info("Running cms migrations...")
run_script(root, config, "cms", "migrate_cms.sh", run_func)
if config["ACTIVATE_FORUM"]:
click.echo(fmt.info("Running forum migrations..."))
fmt.echo_info("Running forum migrations...")
run_script(root, config, "forum", "migrate_forum.sh", run_func)
if config["ACTIVATE_NOTES"]:
click.echo(fmt.info("Running notes migrations..."))
fmt.echo_info("Running notes migrations...")
run_script(root, config, "notes", "migrate_django.sh", run_func)
if config["ACTIVATE_XQUEUE"]:
click.echo(fmt.info("Running xqueue migrations..."))
fmt.echo_info("Running xqueue migrations...")
run_script(root, config, "xqueue", "migrate_django.sh", run_func)
if config["ACTIVATE_LMS"]:
click.echo(fmt.info("Creating oauth2 users..."))
fmt.echo_info("Creating oauth2 users...")
run_script(root, config, "lms", "oauth2.sh", run_func)
click.echo(fmt.info("Databases ready."))
fmt.echo_info("Databases ready.")
def create_user(root, run_func, superuser, staff, name, email):

View File

@ -1,20 +0,0 @@
---
LMS_HOST: "www.myopenedx.com"
CMS_HOST: "studio.{{ LMS_HOST }}"
PLATFORM_NAME: "My Open edX"
CONTACT_EMAIL: "contact@{{ LMS_HOST }}"
LANGUAGE_CODE: "en"
ACTIVATE_HTTPS: false
ACTIVATE_NOTES: false
ACTIVATE_XQUEUE: false
SECRET_KEY: "{{ RAND24 }}"
MYSQL_ROOT_PASSWORD: "{{ RAND8 }}"
OPENEDX_MYSQL_PASSWORD: "{{ RAND8 }}"
NOTES_MYSQL_PASSWORD: "{{ RAND8 }}"
NOTES_SECRET_KEY: "{{ RAND24 }}"
NOTES_OAUTH2_SECRET: "{{ RAND24 }}"
XQUEUE_AUTH_PASSWORD: "{{ RAND8 }}"
XQUEUE_MYSQL_PASSWORD: "{{ RAND8 }}"
XQUEUE_SECRET_KEY: "{{ RAND24 }}"
ANDROID_OAUTH2_SECRET: "{{ RAND24 }}"
ID: "{{ RAND8 }}"