tutor 22.0.2__tar.gz → 22.0.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. {tutor-22.0.2 → tutor-22.0.3}/PKG-INFO +4 -4
  2. {tutor-22.0.2 → tutor-22.0.3}/requirements/base.in +1 -2
  3. {tutor-22.0.2 → tutor-22.0.3}/requirements/dev.txt +2 -2
  4. {tutor-22.0.2 → tutor-22.0.3}/tutor/__about__.py +1 -1
  5. {tutor-22.0.2 → tutor-22.0.3}/tutor/commands/config.py +5 -5
  6. {tutor-22.0.2 → tutor-22.0.3}/tutor/commands/images.py +1 -1
  7. {tutor-22.0.2 → tutor-22.0.3}/tutor/commands/mounts.py +1 -1
  8. {tutor-22.0.2 → tutor-22.0.3}/tutor/commands/params.py +5 -1
  9. {tutor-22.0.2 → tutor-22.0.3}/tutor/commands/plugins.py +2 -2
  10. {tutor-22.0.2 → tutor-22.0.3}/tutor/config.py +25 -0
  11. {tutor-22.0.2 → tutor-22.0.3}/tutor/hooks/catalog.py +5 -1
  12. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/apps/openedx/settings/lms/development.py +1 -3
  13. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/apps/openedx/settings/partials/common_all.py +11 -3
  14. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/apps/openedx/settings/partials/common_cms.py +1 -1
  15. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/apps/openedx/settings/partials/common_lms.py +4 -4
  16. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/build/openedx/Dockerfile +14 -4
  17. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/config/defaults.yml +1 -1
  18. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/jobs/init/mysql.sh +7 -1
  19. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/local/docker-compose.jobs.yml +4 -0
  20. {tutor-22.0.2 → tutor-22.0.3}/.gitignore +0 -0
  21. {tutor-22.0.2 → tutor-22.0.3}/.hatch_build.py +0 -0
  22. {tutor-22.0.2 → tutor-22.0.3}/LICENSE.txt +0 -0
  23. {tutor-22.0.2 → tutor-22.0.3}/README.rst +0 -0
  24. {tutor-22.0.2 → tutor-22.0.3}/pyproject.toml +0 -0
  25. {tutor-22.0.2 → tutor-22.0.3}/requirements/plugins.txt +0 -0
  26. {tutor-22.0.2 → tutor-22.0.3}/tutor/__init__.py +0 -0
  27. {tutor-22.0.2 → tutor-22.0.3}/tutor/bindmount.py +0 -0
  28. {tutor-22.0.2 → tutor-22.0.3}/tutor/commands/__init__.py +0 -0
  29. {tutor-22.0.2 → tutor-22.0.3}/tutor/commands/cli.py +0 -0
  30. {tutor-22.0.2 → tutor-22.0.3}/tutor/commands/compose.py +0 -0
  31. {tutor-22.0.2 → tutor-22.0.3}/tutor/commands/context.py +0 -0
  32. {tutor-22.0.2 → tutor-22.0.3}/tutor/commands/dev.py +0 -0
  33. {tutor-22.0.2 → tutor-22.0.3}/tutor/commands/jobs.py +0 -0
  34. {tutor-22.0.2 → tutor-22.0.3}/tutor/commands/jobs_utils.py +0 -0
  35. {tutor-22.0.2 → tutor-22.0.3}/tutor/commands/k8s.py +0 -0
  36. {tutor-22.0.2 → tutor-22.0.3}/tutor/commands/local.py +0 -0
  37. {tutor-22.0.2 → tutor-22.0.3}/tutor/commands/upgrade/__init__.py +0 -0
  38. {tutor-22.0.2 → tutor-22.0.3}/tutor/commands/upgrade/common.py +0 -0
  39. {tutor-22.0.2 → tutor-22.0.3}/tutor/commands/upgrade/compose.py +0 -0
  40. {tutor-22.0.2 → tutor-22.0.3}/tutor/commands/upgrade/k8s.py +0 -0
  41. {tutor-22.0.2 → tutor-22.0.3}/tutor/core/__init__.py +0 -0
  42. {tutor-22.0.2 → tutor-22.0.3}/tutor/core/hooks/__init__.py +0 -0
  43. {tutor-22.0.2 → tutor-22.0.3}/tutor/core/hooks/actions.py +0 -0
  44. {tutor-22.0.2 → tutor-22.0.3}/tutor/core/hooks/contexts.py +0 -0
  45. {tutor-22.0.2 → tutor-22.0.3}/tutor/core/hooks/filters.py +0 -0
  46. {tutor-22.0.2 → tutor-22.0.3}/tutor/core/hooks/priorities.py +0 -0
  47. {tutor-22.0.2 → tutor-22.0.3}/tutor/env.py +0 -0
  48. {tutor-22.0.2 → tutor-22.0.3}/tutor/exceptions.py +0 -0
  49. {tutor-22.0.2 → tutor-22.0.3}/tutor/fmt.py +0 -0
  50. {tutor-22.0.2 → tutor-22.0.3}/tutor/hooks/__init__.py +0 -0
  51. {tutor-22.0.2 → tutor-22.0.3}/tutor/images.py +0 -0
  52. {tutor-22.0.2 → tutor-22.0.3}/tutor/interactive.py +0 -0
  53. {tutor-22.0.2 → tutor-22.0.3}/tutor/plugins/__init__.py +0 -0
  54. {tutor-22.0.2 → tutor-22.0.3}/tutor/plugins/base.py +0 -0
  55. {tutor-22.0.2 → tutor-22.0.3}/tutor/plugins/indexes.py +0 -0
  56. {tutor-22.0.2 → tutor-22.0.3}/tutor/plugins/openedx.py +0 -0
  57. {tutor-22.0.2 → tutor-22.0.3}/tutor/plugins/v0.py +0 -0
  58. {tutor-22.0.2 → tutor-22.0.3}/tutor/plugins/v1.py +0 -0
  59. {tutor-22.0.2 → tutor-22.0.3}/tutor/py.typed +0 -0
  60. {tutor-22.0.2 → tutor-22.0.3}/tutor/serialize.py +0 -0
  61. {tutor-22.0.2 → tutor-22.0.3}/tutor/tasks.py +0 -0
  62. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/apps/caddy/Caddyfile +0 -0
  63. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/apps/openedx/config/cms.env.yml +0 -0
  64. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/apps/openedx/config/lms.env.yml +0 -0
  65. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/apps/openedx/config/partials/auth.yml +0 -0
  66. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/apps/openedx/settings/cms/__init__.py +0 -0
  67. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/apps/openedx/settings/cms/development.py +0 -0
  68. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/apps/openedx/settings/cms/production.py +0 -0
  69. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/apps/openedx/settings/cms/test.py +0 -0
  70. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/apps/openedx/settings/lms/__init__.py +0 -0
  71. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/apps/openedx/settings/lms/production.py +0 -0
  72. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/apps/openedx/settings/lms/test.py +0 -0
  73. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/apps/openedx/settings/partials/common_test.py +0 -0
  74. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/apps/openedx/uwsgi.ini +0 -0
  75. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/apps/permissions/setowners.sh +0 -0
  76. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/apps/redis/redis.conf +0 -0
  77. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/build/openedx/bin/reload-uwsgi +0 -0
  78. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/build/openedx/revisions.yml +0 -0
  79. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/build/openedx/settings/cms/__init__.py +0 -0
  80. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/build/openedx/settings/cms/assets.py +0 -0
  81. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/build/openedx/settings/cms/i18n.py +0 -0
  82. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/build/openedx/settings/lms/__init__.py +0 -0
  83. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/build/openedx/settings/lms/assets.py +0 -0
  84. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/build/openedx/settings/lms/i18n.py +0 -0
  85. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/build/openedx/settings/partials/assets.py +0 -0
  86. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/build/openedx/settings/partials/i18n.py +0 -0
  87. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/build/openedx/settings/uwsgi.ini +0 -0
  88. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/build/openedx/themes/README +0 -0
  89. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/build/permissions/Dockerfile +0 -0
  90. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/build/permissions/setowner.sh +0 -0
  91. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/config/base.yml +0 -0
  92. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/dev/docker-compose.jobs.yml +0 -0
  93. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/dev/docker-compose.yml +0 -0
  94. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/jobs/init/cms.sh +0 -0
  95. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/jobs/init/lms.sh +0 -0
  96. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/jobs/init/meilisearch.sh +0 -0
  97. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/jobs/init/mounted-directories.sh +0 -0
  98. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/k8s/deployments.yml +0 -0
  99. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/k8s/jobs.yml +0 -0
  100. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/k8s/namespace.yml +0 -0
  101. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/k8s/override.yml +0 -0
  102. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/k8s/services.yml +0 -0
  103. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/k8s/volumes.yml +0 -0
  104. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/kustomization.yml +0 -0
  105. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/local/docker-compose.prod.yml +0 -0
  106. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/local/docker-compose.yml +0 -0
  107. {tutor-22.0.2 → tutor-22.0.3}/tutor/templates/version +0 -0
  108. {tutor-22.0.2 → tutor-22.0.3}/tutor/types.py +0 -0
  109. {tutor-22.0.2 → tutor-22.0.3}/tutor/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: tutor
3
- Version: 22.0.2
3
+ Version: 22.0.3
4
4
  Summary: The Docker-based Open edX distribution designed for peace of mind
5
5
  Project-URL: Homepage, https://docs.tutor.edly.io/
6
6
  Project-URL: Documentation, https://docs.tutor.edly.io/
@@ -24,7 +24,7 @@ Classifier: Programming Language :: Python :: 3.13
24
24
  Classifier: Programming Language :: Python :: 3.14
25
25
  Requires-Python: >=3.10
26
26
  Requires-Dist: appdirs
27
- Requires-Dist: click<8.3.0,>=8.0
27
+ Requires-Dist: click>=8.4
28
28
  Requires-Dist: importlib-metadata>=7.0.1
29
29
  Requires-Dist: importlib-resources>=6.1.1
30
30
  Requires-Dist: jinja2>=2.10
@@ -41,10 +41,10 @@ Requires-Dist: attrs==26.1.0; extra == 'dev'
41
41
  Requires-Dist: backports-tarfile==1.2.0; extra == 'dev'
42
42
  Requires-Dist: build==1.4.4; extra == 'dev'
43
43
  Requires-Dist: cachetools==5.5.2; extra == 'dev'
44
- Requires-Dist: certifi==2025.8.3; extra == 'dev'
44
+ Requires-Dist: certifi==2026.10.19; extra == 'dev'
45
45
  Requires-Dist: charset-normalizer==3.4.7; extra == 'dev'
46
46
  Requires-Dist: click-log==0.4.0; extra == 'dev'
47
- Requires-Dist: click==8.1.8; extra == 'dev'
47
+ Requires-Dist: click==8.5.0; extra == 'dev'
48
48
  Requires-Dist: coverage==7.10.7; extra == 'dev'
49
49
  Requires-Dist: docutils==0.22.4; extra == 'dev'
50
50
  Requires-Dist: durationpy==0.10; extra == 'dev'
@@ -1,6 +1,5 @@
1
1
  appdirs
2
- # TODO: remove 8.3.0 when https://github.com/overhangio/tutor/issues/1276 is resolved completely
3
- click>=8.0,<8.3.0
2
+ click>=8.4
4
3
  jinja2>=2.10
5
4
  kubernetes
6
5
  mypy
@@ -20,7 +20,7 @@ cachetools==5.5.2
20
20
  # via
21
21
  # -r requirements/base.txt
22
22
  # google-auth
23
- certifi==2025.8.3
23
+ certifi==2026.10.19
24
24
  # via
25
25
  # -r requirements/base.txt
26
26
  # kubernetes
@@ -29,7 +29,7 @@ charset-normalizer==3.4.7
29
29
  # via
30
30
  # -r requirements/base.txt
31
31
  # requests
32
- click==8.1.8
32
+ click==8.5.0
33
33
  # via
34
34
  # -r requirements/base.txt
35
35
  # click-log
@@ -2,7 +2,7 @@ import os
2
2
 
3
3
  # Increment this version number to trigger a new release. See
4
4
  # docs/tutor.html#versioning for information on the versioning scheme.
5
- __version__ = "22.0.2"
5
+ __version__ = "22.0.3"
6
6
 
7
7
  # The version suffix will be appended to the actual version, separated by a
8
8
  # dash. Use this suffix to differentiate between the actual released version and
@@ -12,7 +12,7 @@ from tutor import config as tutor_config
12
12
  from tutor import env, exceptions, fmt, hooks, serialize, utils
13
13
  from tutor import interactive as interactive_config
14
14
  from tutor.commands.context import Context
15
- from tutor.commands.params import ConfigLoaderParam
15
+ from tutor.commands.params import ConfigLoaderParam, ParamValue
16
16
  from tutor.types import Config, ConfigValue
17
17
 
18
18
 
@@ -25,7 +25,7 @@ def config_command() -> None:
25
25
  pass
26
26
 
27
27
 
28
- class ConfigKeyParamType(ConfigLoaderParam):
28
+ class ConfigKeyParamType(ConfigLoaderParam[ParamValue]):
29
29
  name = "configkey"
30
30
 
31
31
  def shell_complete(
@@ -49,7 +49,7 @@ class ConfigKeyParamType(ConfigLoaderParam):
49
49
  yield from self.config.items()
50
50
 
51
51
 
52
- class ConfigKeyValParamType(ConfigKeyParamType):
52
+ class ConfigKeyValParamType(ConfigKeyParamType[tuple[str, t.Any]]):
53
53
  """
54
54
  Parser for <KEY>=<YAML VALUE> command line arguments.
55
55
  """
@@ -131,7 +131,7 @@ class ConfigListKeyValParamType(ConfigKeyValParamType):
131
131
  "--unset",
132
132
  "unset_vars",
133
133
  multiple=True,
134
- type=ConfigKeyParamType(),
134
+ type=ConfigKeyParamType[str](),
135
135
  help="Remove a configuration value (can be used multiple times)",
136
136
  )
137
137
  @click.option(
@@ -221,7 +221,7 @@ def printroot(context: Context) -> None:
221
221
 
222
222
 
223
223
  @click.command(help="Print a configuration value")
224
- @click.argument("key", type=ConfigKeyParamType())
224
+ @click.argument("key", type=ConfigKeyParamType[str]())
225
225
  @click.pass_obj
226
226
  def printvalue(context: Context, key: str) -> None:
227
227
  config = tutor_config.load(context.root)
@@ -88,7 +88,7 @@ def _add_core_images_to_push(
88
88
  return remote_images
89
89
 
90
90
 
91
- class ImageNameParam(ConfigLoaderParam):
91
+ class ImageNameParam(ConfigLoaderParam[str]):
92
92
  """
93
93
  Convenient auto-completion of image names.
94
94
  """
@@ -12,7 +12,7 @@ from tutor.commands.context import Context
12
12
  from tutor.commands.params import ConfigLoaderParam
13
13
 
14
14
 
15
- class MountParamType(ConfigLoaderParam):
15
+ class MountParamType(ConfigLoaderParam[str]):
16
16
  name = "mount"
17
17
 
18
18
  def shell_complete(
@@ -6,8 +6,12 @@ from tutor import config as tutor_config
6
6
  from tutor import hooks
7
7
  from tutor.types import Config
8
8
 
9
+ # Type of the value produced by the param type `convert` method. Since click 8.4,
10
+ # `click.ParamType` is generic and must be parameterized with that type.
11
+ ParamValue = t.TypeVar("ParamValue")
9
12
 
10
- class ConfigLoaderParam(click.ParamType):
13
+
14
+ class ConfigLoaderParam(click.ParamType[ParamValue]):
11
15
  """
12
16
  Convenient param child class that automatically loads the user configuration on auto-complete.
13
17
  """
@@ -19,7 +19,7 @@ from tutor.types import Config
19
19
  from .context import Context
20
20
 
21
21
 
22
- class PluginName(click.ParamType):
22
+ class PluginName(click.ParamType[str]):
23
23
  """
24
24
  Convenient param type that supports autocompletion of installed plugin names.
25
25
  """
@@ -44,7 +44,7 @@ class PluginName(click.ParamType):
44
44
  return [name for name in candidates if name.startswith(incomplete)]
45
45
 
46
46
 
47
- class IndexPluginName(click.ParamType):
47
+ class IndexPluginName(click.ParamType[str]):
48
48
  """
49
49
  Param type for auto-completion of plugin names found in index cache.
50
50
  """
@@ -9,6 +9,11 @@ from tutor.types import Config, ConfigValue, cast_config, get_typed
9
9
 
10
10
  CONFIG_FILENAME = "config.yml"
11
11
 
12
+ # Default value of the MEILISEARCH_URL setting. It is duplicated here from
13
+ # config/defaults.yml because loading the configuration defaults is slow. The
14
+ # two values are checked to be in sync by the unit tests.
15
+ MEILISEARCH_URL_DEFAULT = "http://meilisearch:7700"
16
+
12
17
 
13
18
  def load(root: str) -> Config:
14
19
  """
@@ -342,3 +347,23 @@ def _update_enabled_plugins_on_unload(_plugin: str, _root: str, config: Config)
342
347
  Note that this action must be performed after the plugin has been unloaded, hence the low priority.
343
348
  """
344
349
  save_enabled_plugins(config)
350
+
351
+
352
+ @hooks.Actions.CONFIG_LOADED.add()
353
+ def _check_meilisearch_url(config: Config) -> None:
354
+ """
355
+ Warn the user in case Meilisearch is not run by Tutor while MEILISEARCH_URL
356
+ still points at the Tutor-managed instance.
357
+
358
+ Meilisearch is a required component of the Open edX platform: RUN_MEILISEARCH=false
359
+ does not disable it, it only indicates that it is hosted externally.
360
+ """
361
+ if get_typed(config, "RUN_MEILISEARCH", bool, True):
362
+ return
363
+ if get_typed(config, "MEILISEARCH_URL", str, "") == MEILISEARCH_URL_DEFAULT:
364
+ fmt.echo_alert(
365
+ f'Warning: RUN_MEILISEARCH=false, but MEILISEARCH_URL is still set to its default value "{MEILISEARCH_URL_DEFAULT}". '
366
+ "Meilisearch is a required component of the Open edX platform, so RUN_MEILISEARCH=false only means that it is hosted "
367
+ "externally. Set MEILISEARCH_URL to the URL of an existing, preconfigured Meilisearch instance, or set "
368
+ "RUN_MEILISEARCH=true to have Tutor run its own."
369
+ )
@@ -287,6 +287,10 @@ class Filters:
287
287
  DOCKER_BUILD_COMMAND: Filter[list[str], []] = Filter()
288
288
 
289
289
  #: List of patches that should be inserted in a given location of the templates.
290
+ #: Each ``{{ patch("name") }}`` statement in the templates is replaced by the
291
+ #: rendered content of all the patches with the same name. See :ref:`template_patches`
292
+ #: for a detailed explanation and the :ref:`template patch catalog <patches>` for
293
+ #: the list of all available patches.
290
294
  #:
291
295
  #: :parameter list[tuple[str, str]] patches: pairs of (name, content) tuples. Use this
292
296
  #: filter to modify the Tutor templates.
@@ -325,7 +329,7 @@ class Filters:
325
329
  #: - ``list_if``: In a list of ``(value, condition)`` tuples, return the list of ``value`` for which the ``condition`` is true.
326
330
  #: - ``long_to_base64``: Base-64 encode a long integer.
327
331
  #: - ``iter_values_named``: Yield the values of the configuration settings that match a certain pattern. Example: ``{% for value in iter_values_named(prefix="KEY", suffix="SUFFIX")%}...{% endfor %}``. By default, only non-empty values are yielded. To iterate also on empty values, pass the ``allow_empty=True`` argument.
328
- #: - ``patch``: See :ref:`patches <v0_plugin_patches>`.
332
+ #: - ``patch``: See :ref:`template_patches`.
329
333
  #: - ``random_string``: Return a random string of the given length composed of ASCII letters and digits. Example: ``{{ 8|random_string }}``.
330
334
  #: - ``reverse_host``: Reverse a domain name (see `reference <https://en.wikipedia.org/wiki/Reverse_domain_name_notation>`__). Example: ``{{ "demo.myopenedx.com"|reverse_host }}`` is equal to "com.myopenedx.demo".
331
335
  #: - ``rsa_import_key``: Import a PEM-formatted RSA key and return the corresponding object.
@@ -26,10 +26,8 @@ SESSION_COOKIE_SAMESITE = "Lax"
26
26
  # CMS authentication
27
27
  IDA_LOGOUT_URI_LIST.append("http://{{ CMS_HOST }}:8001/logout/")
28
28
 
29
- FEATURES["ENABLE_COURSEWARE_MICROFRONTEND"] = False
30
-
31
29
  # Disable enterprise integration
32
- FEATURES["ENABLE_ENTERPRISE_INTEGRATION"] = False
30
+ ENABLE_ENTERPRISE_INTEGRATION = False
33
31
  SYSTEM_WIDE_ROLE_CLASSES.remove("enterprise.SystemWideEnterpriseUserRoleAssignment")
34
32
 
35
33
  LOGGING["loggers"]["oauth2_provider"] = {
@@ -4,6 +4,14 @@ import os
4
4
 
5
5
  from xmodule.modulestore.modulestore_settings import update_module_store_settings
6
6
 
7
+ # Re-bind the FEATURES proxy to this settings module, so that both flat feature
8
+ # settings and legacy FEATURES["..."] reads and writes resolve here.
9
+ try:
10
+ from openedx.core.lib.features_setting_proxy import FeaturesProxy
11
+ FEATURES = FeaturesProxy(globals())
12
+ except ImportError:
13
+ pass
14
+
7
15
  # Mongodb connection parameters: simply modify `mongodb_parameters` to affect all connections to MongoDb.
8
16
  mongodb_parameters = {
9
17
  "db": "{{ MONGODB_DATABASE }}",
@@ -229,9 +237,9 @@ JWT_AUTH["JWT_ISSUERS"] = [
229
237
  ]
230
238
 
231
239
  # Enable/Disable some features globally
232
- FEATURES["ENABLE_DISCUSSION_SERVICE"] = False
233
- FEATURES["PREVENT_CONCURRENT_LOGINS"] = False
234
- FEATURES["ENABLE_CORS_HEADERS"] = True
240
+ ENABLE_DISCUSSION_SERVICE = False
241
+ PREVENT_CONCURRENT_LOGINS = False
242
+ ENABLE_CORS_HEADERS = True
235
243
 
236
244
  # CORS
237
245
  CORS_ALLOW_CREDENTIALS = True
@@ -21,7 +21,7 @@ FRONTEND_LOGIN_URL = LMS_ROOT_URL + '/login'
21
21
  FRONTEND_REGISTER_URL = LMS_ROOT_URL + '/register'
22
22
 
23
23
  # Enable "reindex" button
24
- FEATURES["ENABLE_COURSEWARE_INDEX"] = True
24
+ ENABLE_COURSEWARE_INDEX = True
25
25
 
26
26
  # Create folders if necessary
27
27
  for folder in [LOG_DIR, MEDIA_ROOT, STATIC_ROOT, ORA2_FILEUPLOAD_ROOT]:
@@ -21,7 +21,7 @@ OAUTH_ENFORCE_SECURE = False
21
21
  # Email settings
22
22
  DEFAULT_EMAIL_LOGO_URL = LMS_ROOT_URL + "/theming/asset/images/logo.png"
23
23
  BULK_EMAIL_SEND_USING_EDX_ACE = True
24
- FEATURES["ENABLE_FOOTER_MOBILE_APP_LINKS"] = False
24
+ ENABLE_FOOTER_MOBILE_APP_LINKS = False
25
25
 
26
26
  # Branding
27
27
  MOBILE_STORE_ACE_URLS = {}
@@ -38,9 +38,9 @@ CACHES["staticfiles"] = {
38
38
  }
39
39
 
40
40
  # Enable search features
41
- FEATURES["ENABLE_COURSE_DISCOVERY"] = True
42
- FEATURES["ENABLE_COURSEWARE_SEARCH"] = True
43
- FEATURES["ENABLE_DASHBOARD_SEARCH"] = True
41
+ ENABLE_COURSE_DISCOVERY = True
42
+ ENABLE_COURSEWARE_SEARCH = True
43
+ ENABLE_DASHBOARD_SEARCH = True
44
44
 
45
45
  # Create folders if necessary
46
46
  for folder in [DATA_DIR, LOG_DIR, MEDIA_ROOT, STATIC_ROOT, ORA2_FILEUPLOAD_ROOT]:
@@ -8,10 +8,10 @@ ENV DEBIAN_FRONTEND=noninteractive
8
8
  RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
9
9
  --mount=type=cache,target=/var/lib/apt,sharing=locked \
10
10
  apt update && \
11
- apt install -y build-essential curl git language-pack-en
11
+ apt install -y build-essential ca-certificates curl git language-pack-en
12
12
  ENV LC_ALL=en_US.UTF-8
13
13
 
14
- COPY --from=ghcr.io/astral-sh/uv:0.9.18 /uv /usr/local/bin/uv
14
+ COPY --from=ghcr.io/astral-sh/uv:0.13.5 /uv /usr/local/bin/uv
15
15
  ARG PIP_COMMAND="uv pip"
16
16
 
17
17
  # Constrain the build-time setuptools used in the isolated (PEP 517) build
@@ -23,6 +23,8 @@ ARG PIP_COMMAND="uv pip"
23
23
  RUN echo "setuptools==80.9.0" > /etc/openedx-build-constraints.txt
24
24
  ENV UV_BUILD_CONSTRAINT=/etc/openedx-build-constraints.txt
25
25
  ENV PIP_CONSTRAINT=/etc/openedx-build-constraints.txt
26
+ # pip 26 no longer applies PIP_CONSTRAINT to build dependencies
27
+ ENV PIP_BUILD_CONSTRAINT=/etc/openedx-build-constraints.txt
26
28
 
27
29
  {{ patch("openedx-dockerfile-minimal") }}
28
30
 
@@ -110,7 +112,7 @@ RUN --mount=type=cache,target=/openedx/.cache/pip,sharing=shared \
110
112
  # https://pypi.org/project/setuptools/
111
113
  # https://pypi.org/project/pip/
112
114
  # https://pypi.org/project/wheel/
113
- setuptools pip==24.0 wheel==0.43.0
115
+ setuptools pip==26.2.2 wheel==0.43.0
114
116
 
115
117
  # Install base requirements and asset-building requirements
116
118
  RUN --mount=type=bind,from=edx-platform,source=/requirements/edx/base.txt,target=/openedx/edx-platform/requirements/edx/base.txt \
@@ -118,6 +120,11 @@ RUN --mount=type=bind,from=edx-platform,source=/requirements/edx/base.txt,target
118
120
  --mount=type=cache,target=/openedx/.cache/pip,sharing=shared \
119
121
  $PIP_COMMAND install -r /openedx/edx-platform/requirements/edx/base.txt -r /openedx/edx-platform/requirements/edx/assets.txt
120
122
 
123
+ # Upgrade certifi until edx-platform pins a version without the compromised emSign roots
124
+ # https://emudhra.we-have-a-problem.com/
125
+ RUN --mount=type=cache,target=/openedx/.cache/pip,sharing=shared \
126
+ $PIP_COMMAND install "certifi>=2026.10.19"
127
+
121
128
  # Install extra requirements
122
129
  RUN --mount=type=cache,target=/openedx/.cache/pip,sharing=shared \
123
130
  $PIP_COMMAND install \
@@ -150,7 +157,7 @@ ENV PATH=/openedx/nodeenv/bin:/openedx/venv/bin:${PATH}
150
157
  # Install nodeenv with the version provided by edx-platform
151
158
  # https://github.com/openedx/edx-platform/blob/master/requirements/edx/assets.txt
152
159
  RUN $PIP_COMMAND install nodeenv==1.9.1
153
- RUN nodeenv /openedx/nodeenv --node=24.16.0 --prebuilt
160
+ RUN nodeenv /openedx/nodeenv --node=24.22.1 --prebuilt
154
161
 
155
162
  # Install nodejs requirements
156
163
  ARG NPM_REGISTRY={{ NPM_REGISTRY }}
@@ -293,6 +300,9 @@ USER app
293
300
  # Install dev python requirements
294
301
  RUN --mount=type=cache,target=/openedx/.cache/pip,sharing=shared \
295
302
  $PIP_COMMAND install -r requirements/edx/development.txt
303
+ # development.txt pins the old certifi again, so upgrade it one more time
304
+ RUN --mount=type=cache,target=/openedx/.cache/pip,sharing=shared \
305
+ $PIP_COMMAND install "certifi>=2026.10.19"
296
306
  # https://pypi.org/project/ipdb/
297
307
  # https://pypi.org/project/ipython (>=Python 3.10 started with 8.20)
298
308
  RUN --mount=type=cache,target=/openedx/.cache/pip,sharing=shared \
@@ -15,7 +15,7 @@ DOCKER_REGISTRY: "docker.io/"
15
15
  DOCKER_IMAGE_OPENEDX: "{{ DOCKER_REGISTRY }}overhangio/openedx:{{ TUTOR_VERSION }}"
16
16
  DOCKER_IMAGE_OPENEDX_DEV: "openedx-dev:{{ TUTOR_VERSION }}"
17
17
  # https://hub.docker.com/_/caddy/tags
18
- DOCKER_IMAGE_CADDY: "docker.io/caddy:2.11.4"
18
+ DOCKER_IMAGE_CADDY: "docker.io/caddy:2.11.7"
19
19
  # https://hub.docker.com/r/getmeili/meilisearch/tags
20
20
  DOCKER_IMAGE_MEILISEARCH: "docker.io/getmeili/meilisearch:v1.36.0"
21
21
  # https://hub.docker.com/_/mongo/tags
@@ -16,6 +16,12 @@ echo "MySQL is up and running"
16
16
 
17
17
  # edx-platform database
18
18
  mysql -u {{ MYSQL_ROOT_USERNAME }} --password="{{ MYSQL_ROOT_PASSWORD }}" --host "{{ MYSQL_HOST }}" --port {{ MYSQL_PORT }} -e "CREATE DATABASE IF NOT EXISTS {{ OPENEDX_MYSQL_DATABASE }};"
19
- mysql -u {{ MYSQL_ROOT_USERNAME }} --password="{{ MYSQL_ROOT_PASSWORD }}" --host "{{ MYSQL_HOST }}" --port {{ MYSQL_PORT }} -e "CREATE USER IF NOT EXISTS '{{ OPENEDX_MYSQL_USERNAME }}';"
19
+ # This command may fail if the specified user has created a custom view, such
20
+ # as is done in the enterprise app.
21
+ #
22
+ # Yes, even 'if not exists' does not prevent this. :/
23
+ # https://bugs.mysql.com/bug.php?id=107139
24
+ mysql -u {{ MYSQL_ROOT_USERNAME }} --password="{{ MYSQL_ROOT_PASSWORD }}" --host "{{ MYSQL_HOST }}" --port {{ MYSQL_PORT }} -e "CREATE USER IF NOT EXISTS '{{ OPENEDX_MYSQL_USERNAME }}';" || true
25
+ # If the user truly doesn't exist, we'll get an error here.
20
26
  mysql -u {{ MYSQL_ROOT_USERNAME }} --password="{{ MYSQL_ROOT_PASSWORD }}" --host "{{ MYSQL_HOST }}" --port {{ MYSQL_PORT }} -e "ALTER USER '{{ OPENEDX_MYSQL_USERNAME }}'@'%' IDENTIFIED BY '{{ OPENEDX_MYSQL_PASSWORD }}';"
21
27
  mysql -u {{ MYSQL_ROOT_USERNAME }} --password="{{ MYSQL_ROOT_PASSWORD }}" --host "{{ MYSQL_HOST }}" --port {{ MYSQL_PORT }} -e "GRANT ALL ON {{ OPENEDX_MYSQL_DATABASE }}.* TO '{{ OPENEDX_MYSQL_USERNAME }}'@'%';"
@@ -27,6 +27,8 @@ services:
27
27
  - ../apps/openedx/settings/cms:/openedx/edx-platform/cms/envs/tutor:roz
28
28
  - ../apps/openedx/config:/openedx/config:roz
29
29
  - ../../data/lms-job:/openedx/data:z
30
+ - ../../data/openedx-media:/openedx/media:z
31
+ - ../../data/openedx-media-private:/openedx/media-private:z
30
32
  {%- for mount in iter_mounts(MOUNTS, "openedx", "lms-job") %}
31
33
  - {{ mount }}:z
32
34
  {%- endfor %}
@@ -42,6 +44,8 @@ services:
42
44
  - ../apps/openedx/settings/cms:/openedx/edx-platform/cms/envs/tutor:roz
43
45
  - ../apps/openedx/config:/openedx/config:roz
44
46
  - ../../data/cms-job:/openedx/data:z
47
+ - ../../data/openedx-media:/openedx/media:z
48
+ - ../../data/openedx-media-private:/openedx/media-private:z
45
49
  {%- for mount in iter_mounts(MOUNTS, "openedx", "cms-job") %}
46
50
  - {{ mount }}:z
47
51
  {%- endfor %}
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes