Skip to content

Commit 9080eb5

Browse files
Alec4rbrian-smith-tcril
authored andcommitted
feat: create theme folders from config and document structure
1 parent 86aae16 commit 9080eb5

File tree

2 files changed

+76
-171
lines changed

2 files changed

+76
-171
lines changed
Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
paragon plugin for `Tutor <https://docs.tutor.edly.io>`__
2-
#########################################################
1+
Paragon plugin for `Tutor <https://docs.tutor.edly.io>`__
2+
###########################################################
33

4-
Facilitates the generation and static hosting of Paragon-based theme CSS files for Open edX MFEs using Tutor.
4+
Facilitates the generation and static hosting of Paragon-based CSS themes for Open edX Micro-Frontend (MFE) applications using `Paragon <https://openedx.github.io/paragon/>`__.
55

6+
This plugin provides a local folder structure to manage theme source files and compile them into CSS, enabling flexible customization of Open edX MFEs via Tutor.
67

78
Installation
89
************
@@ -11,15 +12,52 @@ Installation
1112
1213
pip install git+https://github.com/openedx/openedx-tutor-plugins.git#subdirectory=plugins/tutor-contrib-paragon
1314
14-
Usage
15-
*****
15+
For development:
16+
17+
.. code-block:: bash
18+
19+
cd openedx-tutor-plugins/plugins/tutor-contrib-paragon
20+
pip install -e .
21+
22+
Enable the plugin:
1623

1724
.. code-block:: bash
1825
1926
tutor plugins enable paragon
2027
28+
Directory Structure
29+
*******************
30+
31+
The plugin will create the following structure inside your Tutor environment:
32+
33+
.. code-block::
34+
35+
tutor/env/plugins/paragon/
36+
├── theme-sources/ # Place your style dictionary folders here (e.g. theme-xyz/)
37+
└── compiled-themes/ # Compiled CSS files go here, ready for static hosting
38+
39+
Each theme inside `theme-sources/` should follow the Style Dictionary folder structure.
40+
Only themes listed in `PARAGON_ENABLED_THEMES` will be compiled.
41+
42+
The `compiled-themes/` folder is designed to be exposed via static web servers such as **nginx** or **Caddy**, or can be uploaded to cloud hosting providers like **Amazon S3** or **Cloudflare Pages**.
43+
44+
Configuration
45+
*************
46+
47+
All configuration variables can be overridden via `tutor config save`:
48+
49+
.. code-block:: yaml
50+
51+
PARAGON_THEME_SOURCES_PATH: "env/plugins/paragon/theme-sources"
52+
PARAGON_COMPILED_THEMES_PATH: "env/plugins/paragon/compiled-themes"
53+
PARAGON_ENABLED_THEMES:
54+
- theme-1
55+
- theme-2
56+
PARAGON_SERVE_COMPILED_THEMES: true
57+
58+
You may customize paths or theme names to suit your deployment.
2159

2260
License
2361
*******
2462

25-
This software is licensed under the terms of the AGPLv3.
63+
This software is licensed under the terms of the AGPLv3.

plugins/tutor-contrib-paragon/tutorparagon/plugin.py

Lines changed: 32 additions & 165 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import click
55
import importlib_resources
66
from tutor import hooks
7+
from tutor import config as tutor_config
78

89
from .__about__ import __version__
910

@@ -13,112 +14,45 @@
1314

1415
hooks.Filters.CONFIG_DEFAULTS.add_items(
1516
[
16-
# Add your new settings that have default values here.
17-
# Each new setting is a pair: (setting_name, default_value).
18-
# Prefix your setting names with 'PARAGON_'.
17+
# Plugin version (used for compatibility or debugging)
1918
("PARAGON_VERSION", __version__),
19+
# Directory where users will place style-dictionary source files
20+
# Each subfolder inside will represent a theme (e.g., "theme-abc/")
21+
("PARAGON_THEME_SOURCES_PATH", "env/plugins/paragon/theme-sources"),
22+
# Directory where compiled CSS themes will be stored after transformation
23+
# One subfolder per compiled theme (e.g., "theme-abc/core.min.css")
24+
("PARAGON_COMPILED_THEMES_PATH", "env/plugins/paragon/compiled-themes"),
25+
# List of enabled themes to compile and serve
26+
# Only themes listed here will be processed, even if others exist in sources
27+
("PARAGON_ENABLED_THEMES", []),
28+
# Whether Tutor should expose the compiled themes to be served (e.g. via nginx, cady or static server)
29+
("PARAGON_SERVE_COMPILED_THEMES", True),
2030
]
2131
)
2232

23-
hooks.Filters.CONFIG_UNIQUE.add_items(
24-
[
25-
# Add settings that don't have a reasonable default for all users here.
26-
# For instance: passwords, secret keys, etc.
27-
# Each new setting is a pair: (setting_name, unique_generated_value).
28-
# Prefix your setting names with 'PARAGON_'.
29-
# For example:
30-
### ("PARAGON_SECRET_KEY", "{{ 24|random_string }}"),
31-
]
32-
)
33-
34-
hooks.Filters.CONFIG_OVERRIDES.add_items(
35-
[
36-
# Danger zone!
37-
# Add values to override settings from Tutor core or other plugins here.
38-
# Each override is a pair: (setting_name, new_value). For example:
39-
### ("PLATFORM_NAME", "My platform"),
40-
]
41-
)
4233

34+
# Create directories for build and host
35+
@hooks.Actions.PROJECT_ROOT_READY.add()
36+
def create_paragon_folders(project_root: str) -> None:
37+
config = tutor_config.load(project_root)
4338

44-
########################################
45-
# INITIALIZATION TASKS
46-
########################################
47-
48-
# To add a custom initialization task, create a bash script template under:
49-
# tutorparagon/templates/paragon/tasks/
50-
# and then add it to the MY_INIT_TASKS list. Each task is in the format:
51-
# ("<service>", ("<path>", "<to>", "<script>", "<template>"))
52-
MY_INIT_TASKS: list[tuple[str, tuple[str, ...]]] = [
53-
# For example, to add LMS initialization steps, you could add the script template at:
54-
# tutorparagon/templates/paragon/tasks/lms/init.sh
55-
# And then add the line:
56-
### ("lms", ("paragon", "tasks", "lms", "init.sh")),
57-
]
58-
59-
60-
# For each task added to MY_INIT_TASKS, we load the task template
61-
# and add it to the CLI_DO_INIT_TASKS filter, which tells Tutor to
62-
# run it as part of the `init` job.
63-
for service, template_path in MY_INIT_TASKS:
64-
full_path: str = str(
65-
importlib_resources.files("tutorparagon")
66-
/ os.path.join("templates", *template_path)
39+
# Paths from config (always have defaults)
40+
theme_sources_path = os.path.join(
41+
project_root, str(config["PARAGON_THEME_SOURCES_PATH"])
42+
)
43+
compiled_themes_path = os.path.join(
44+
project_root, str(config["PARAGON_COMPILED_THEMES_PATH"])
6745
)
68-
with open(full_path, encoding="utf-8") as init_task_file:
69-
init_task: str = init_task_file.read()
70-
hooks.Filters.CLI_DO_INIT_TASKS.add_item((service, init_task))
71-
72-
73-
########################################
74-
# DOCKER IMAGE MANAGEMENT
75-
########################################
76-
77-
78-
# Images to be built by `tutor images build`.
79-
# Each item is a quadruple in the form:
80-
# ("<tutor_image_name>", ("path", "to", "build", "dir"), "<docker_image_tag>", "<build_args>")
81-
hooks.Filters.IMAGES_BUILD.add_items(
82-
[
83-
# To build `myimage` with `tutor images build myimage`,
84-
# you would add a Dockerfile to templates/paragon/build/myimage,
85-
# and then write:
86-
### (
87-
### "myimage",
88-
### ("plugins", "paragon", "build", "myimage"),
89-
### "docker.io/myimage:{{ PARAGON_VERSION }}",
90-
### (),
91-
### ),
92-
]
93-
)
9446

95-
96-
# Images to be pulled as part of `tutor images pull`.
97-
# Each item is a pair in the form:
98-
# ("<tutor_image_name>", "<docker_image_tag>")
99-
hooks.Filters.IMAGES_PULL.add_items(
100-
[
101-
# To pull `myimage` with `tutor images pull myimage`, you would write:
102-
### (
103-
### "myimage",
104-
### "docker.io/myimage:{{ PARAGON_VERSION }}",
105-
### ),
106-
]
107-
)
108-
109-
110-
# Images to be pushed as part of `tutor images push`.
111-
# Each item is a pair in the form:
112-
# ("<tutor_image_name>", "<docker_image_tag>")
113-
hooks.Filters.IMAGES_PUSH.add_items(
114-
[
115-
# To push `myimage` with `tutor images push myimage`, you would write:
116-
### (
117-
### "myimage",
118-
### "docker.io/myimage:{{ PARAGON_VERSION }}",
119-
### ),
120-
]
121-
)
47+
for path, label in [
48+
(theme_sources_path, "Theme Shources"),
49+
(compiled_themes_path, "Compiled Themes"),
50+
]:
51+
if os.path.exists(path):
52+
print(f"[paragon] {label} folder already exists at: {path}")
53+
else:
54+
os.makedirs(path, exist_ok=True)
55+
print(f"[paragon] Created {label} folder at: {path}")
12256

12357

12458
########################################
@@ -158,70 +92,3 @@
15892
for path in glob(str(importlib_resources.files("tutorparagon") / "patches" / "*")):
15993
with open(path, encoding="utf-8") as patch_file:
16094
hooks.Filters.ENV_PATCHES.add_item((os.path.basename(path), patch_file.read()))
161-
162-
163-
########################################
164-
# CUSTOM JOBS (a.k.a. "do-commands")
165-
########################################
166-
167-
# A job is a set of tasks, each of which run inside a certain container.
168-
# Jobs are invoked using the `do` command, for example: `tutor local do importdemocourse`.
169-
# A few jobs are built in to Tutor, such as `init` and `createuser`.
170-
# You can also add your own custom jobs:
171-
172-
173-
# To add a custom job, define a Click command that returns a list of tasks,
174-
# where each task is a pair in the form ("<service>", "<shell_command>").
175-
# For example:
176-
### @click.command()
177-
### @click.option("-n", "--name", default="plugin developer")
178-
### def say_hi(name: str) -> list[tuple[str, str]]:
179-
### """
180-
### An example job that just prints 'hello' from within both LMS and CMS.
181-
### """
182-
### return [
183-
### ("lms", f"echo 'Hello from LMS, {name}!'"),
184-
### ("cms", f"echo 'Hello from CMS, {name}!'"),
185-
### ]
186-
187-
188-
# Then, add the command function to CLI_DO_COMMANDS:
189-
## hooks.Filters.CLI_DO_COMMANDS.add_item(say_hi)
190-
191-
# Now, you can run your job like this:
192-
# $ tutor local do say-hi --name="Alejandro Cardenas"
193-
194-
195-
#######################################
196-
# CUSTOM CLI COMMANDS
197-
#######################################
198-
199-
# Your plugin can also add custom commands directly to the Tutor CLI.
200-
# These commands are run directly on the user's host computer
201-
# (unlike jobs, which are run in containers).
202-
203-
# To define a command group for your plugin, you would define a Click
204-
# group and then add it to CLI_COMMANDS:
205-
206-
207-
### @click.group()
208-
### def paragon() -> None:
209-
### pass
210-
211-
212-
### hooks.Filters.CLI_COMMANDS.add_item(paragon)
213-
214-
215-
# Then, you would add subcommands directly to the Click group, for example:
216-
217-
218-
### @paragon.command()
219-
### def example_command() -> None:
220-
### """
221-
### This is helptext for an example command.
222-
### """
223-
### print("You've run an example command.")
224-
225-
226-
# This would allow you to run:
227-
# $ tutor paragon example-command

0 commit comments

Comments
 (0)