forked from oppia/oppia
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstart.py
More file actions
205 lines (175 loc) · 7.68 KB
/
start.py
File metadata and controls
205 lines (175 loc) · 7.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# Copyright 2019 The Oppia Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the 'License');
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an 'AS-IS' BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""This script starts up a development server running Oppia. It installs any
missing third-party dependencies and starts up a local GAE development
server.
"""
from __future__ import annotations
import argparse
import contextlib
import time
from typing import Iterator, Optional, Sequence
# Do not import any Oppia modules here,
# import them below the "install_third_party_libs.main()" line.
from . import install_third_party_libs
# This installs third party libraries before importing other files or importing
# libraries that use the builtins python module (e.g. build).
install_third_party_libs.main()
from . import build # isort:skip pylint: disable=wrong-import-position, wrong-import-order
from . import common # isort:skip pylint: disable=wrong-import-position, wrong-import-order
from . import extend_index_yaml # isort:skip pylint: disable=wrong-import-position, wrong-import-order
from . import servers # isort:skip pylint: disable=wrong-import-position, wrong-import-order
from core.constants import constants # isort:skip pylint: disable=wrong-import-position, wrong-import-order
_PARSER = argparse.ArgumentParser(
description="""
Run the script from the oppia root folder:
python -m scripts.start
Note that the root folder MUST be named 'oppia'.
""")
_PARSER.add_argument(
'--save_datastore',
help='optional; if specified, does not clear the datastore.',
action='store_true')
_PARSER.add_argument(
'--disable_host_checking',
help='optional; if specified, disables host checking so that the dev '
'server can be accessed by any device on the same network using the '
'host device\'s IP address. DO NOT use this flag if you\'re running '
'on an untrusted network.',
action='store_true')
_PARSER.add_argument(
'--prod_env',
help='optional; if specified, runs Oppia in a production environment.',
action='store_true')
_PARSER.add_argument(
'--maintenance_mode',
help='optional; if specified, puts Oppia into maintenance mode.',
action='store_true')
_PARSER.add_argument(
'--no_browser',
help='optional; if specified, does not open a browser.',
action='store_true')
_PARSER.add_argument(
'--no_auto_restart',
help='optional; if specified, does not automatically restart when files '
'are changed.',
action='store_true')
_PARSER.add_argument(
'--source_maps',
help='optional; if specified, build webpack with source maps.',
action='store_true')
PORT_NUMBER_FOR_GAE_SERVER = 8181
@contextlib.contextmanager
def alert_on_exit() -> Iterator[None]:
"""Context manager that alerts developers to wait for a graceful shutdown.
Yields:
None. Nothing.
"""
try:
yield
finally:
print(
'\n\n'
# ANSI escape sequence for bright yellow text color.
'\033[93m'
# ANSI escape sequence for bold font.
'\033[1m'
'Servers are shutting down, please wait for them to end gracefully!'
# ANSI escape sequence for resetting formatting.
'\033[0m'
'\n\n')
# Give developers an opportunity to read the alert.
time.sleep(5)
def notify_about_successful_shutdown() -> None:
"""Notifies developers that the servers have shutdown gracefully."""
print(
'\n\n'
# ANSI escape sequence for bright green text color.
'\033[92m'
# ANSI escape sequence for bold font.
'\033[1m'
# The notification.
'Done! Thank you for waiting.'
# ANSI escape sequence for resetting formatting.
'\033[0m'
'\n\n')
def call_extend_index_yaml() -> None:
"""Calls the extend_index_yaml.py script."""
print('\033[94m' + 'Extending index.yaml...' + '\033[0m')
extend_index_yaml.main()
def main(args: Optional[Sequence[str]] = None) -> None:
"""Starts up a development server running Oppia."""
parsed_args = _PARSER.parse_args(args=args)
if common.is_port_in_use(PORT_NUMBER_FOR_GAE_SERVER):
common.print_each_string_after_two_new_lines([
'WARNING',
'Could not start new server. There is already an existing server '
'running at port %s.' % PORT_NUMBER_FOR_GAE_SERVER,
])
# NOTE: The ordering of alert_on_exit() is important because we want the
# alert to be printed _before_ the ExitStack unwinds, hence its placement as
# the "latter" context (context managers exit in reverse-order).
with contextlib.ExitStack() as stack, alert_on_exit():
# ExitStack unwinds in reverse-order, so this will be the final action.
stack.callback(notify_about_successful_shutdown)
stack.callback(call_extend_index_yaml)
build_args = []
if parsed_args.prod_env:
build_args.append('--prod_env')
if parsed_args.maintenance_mode:
build_args.append('--maintenance_mode')
if parsed_args.source_maps:
build_args.append('--source_maps')
build.main(args=build_args)
stack.callback(build.set_constants_to_default)
stack.enter_context(servers.managed_redis_server())
stack.enter_context(servers.managed_elasticsearch_dev_server())
if constants.EMULATOR_MODE:
stack.enter_context(servers.managed_firebase_auth_emulator(
recover_users=parsed_args.save_datastore))
stack.enter_context(servers.managed_cloud_datastore_emulator(
clear_datastore=not parsed_args.save_datastore))
# NOTE: When prod_env=True the Webpack compiler is run by build.main().
if not parsed_args.prod_env:
stack.enter_context(servers.managed_webpack_compiler(
use_prod_env=False, use_source_maps=parsed_args.source_maps,
watch_mode=True))
app_yaml_path = 'app.yaml' if parsed_args.prod_env else 'app_dev.yaml'
dev_appserver = stack.enter_context(servers.managed_dev_appserver(
app_yaml_path,
enable_host_checking=not parsed_args.disable_host_checking,
automatic_restart=not parsed_args.no_auto_restart,
skip_sdk_update_check=True,
port=PORT_NUMBER_FOR_GAE_SERVER))
managed_web_browser = (
None if parsed_args.no_browser else
servers.create_managed_web_browser(PORT_NUMBER_FOR_GAE_SERVER)) # type: ignore[no-untyped-call]
if managed_web_browser is None:
common.print_each_string_after_two_new_lines([
'INFORMATION',
'Local development server is ready! You can access it by '
'navigating to http://localhost:%s/ in a web '
'browser.' % PORT_NUMBER_FOR_GAE_SERVER,
])
else:
common.print_each_string_after_two_new_lines([
'INFORMATION',
'Local development server is ready! Opening a default web '
'browser window pointing to it: '
'http://localhost:%s/' % PORT_NUMBER_FOR_GAE_SERVER,
])
stack.enter_context(managed_web_browser)
dev_appserver.wait()
if __name__ == '__main__': # pragma: no cover
main()