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:
parent
407659ff06
commit
0199a1e916
@ -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"
|
||||
|
@ -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"
|
||||
)
|
||||
|
@ -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")
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
16
tutor/fmt.py
16
tutor/fmt.py
@ -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)
|
||||
|
@ -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):
|
||||
|
@ -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 }}"
|
Loading…
x
Reference in New Issue
Block a user