-
Notifications
You must be signed in to change notification settings - Fork 11
Milestone 1: Plugin Scaffolding and Configuration Setup #33
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
brian-smith-tcril
merged 4 commits into
openedx:main
from
eduNEXT:dam/plugin_scaffolding
May 28, 2025
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
aa6a892
chore: add CI workflow and local development tooling
Alec4r ea00605
feat: create theme folders from config and document structure
Alec4r a83c2fb
docs: clarify theme structure and custom CSS guidance
Alec4r bc0ab92
docs: add note about pending documentation for theme extensions
Alec4r File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| # Workflow dedicated to testing the tutor-contrib-paragon plugin only | ||
| name: Test - tutor-contrib-paragon | ||
|
|
||
| on: | ||
| pull_request: | ||
| paths: | ||
| # Trigger this workflow only if files change inside this plugin folder | ||
| - 'plugins/tutor-contrib-paragon/**' | ||
|
|
||
| jobs: | ||
| test-paragon: | ||
| name: Run tests for tutor-contrib-paragon | ||
| runs-on: ubuntu-latest | ||
| defaults: | ||
| run: | ||
| # All steps will run from this directory | ||
| working-directory: plugins/tutor-contrib-paragon | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - name: Set up Python | ||
| uses: actions/setup-python@v4 | ||
| with: | ||
| python-version: "3.12" | ||
|
|
||
| - name: Install dependencies | ||
| run: | | ||
| python -m pip install --upgrade pip | ||
| make dev-requirements | ||
|
|
||
| - name: Run tests | ||
| run: make run-tests |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| .*.swp | ||
| !.gitignore | ||
| TODO | ||
| __pycache__ | ||
| *.egg-info/ | ||
| /build/ | ||
| /dist/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| # https://hatch.pypa.io/latest/how-to/config/dynamic-metadata/ | ||
|
|
||
| import os | ||
| import typing as t | ||
|
|
||
| from hatchling.metadata.plugin.interface import MetadataHookInterface | ||
|
|
||
| HERE = os.path.dirname(__file__) | ||
|
|
||
|
|
||
| class MetaDataHook(MetadataHookInterface): | ||
| def update(self, metadata: dict[str, t.Any]) -> None: | ||
| about = load_about() | ||
| metadata["version"] = about["__version__"] | ||
|
|
||
|
|
||
| def load_about() -> dict[str, str]: | ||
| about: dict[str, str] = {} | ||
| with open(os.path.join(HERE, "tutorparagon", "__about__.py"), "rt", encoding="utf-8") as f: | ||
| exec(f.read(), about) # pylint: disable=exec-used | ||
| return about |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| recursive-include tutorparagon/patches * | ||
| recursive-include tutorparagon/templates * |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| .DEFAULT_GOAL := help | ||
| .PHONY: docs | ||
| SRC_DIRS = ./tutorparagon | ||
| BLACK_OPTS = --exclude templates ${SRC_DIRS} | ||
|
|
||
| # Warning: These checks are not necessarily run on every PR. | ||
| test: test-lint test-types test-format # Run some static checks. | ||
|
|
||
| test-format: ## Run code formatting tests | ||
| black --check --diff $(BLACK_OPTS) | ||
|
|
||
| test-lint: ## Run code linting tests | ||
| pylint --errors-only --enable=unused-import,unused-argument --ignore=templates --ignore=docs/_ext ${SRC_DIRS} | ||
|
|
||
| test-types: ## Run type checks. | ||
| mypy --exclude=templates --ignore-missing-imports --implicit-reexport --strict ${SRC_DIRS} | ||
|
|
||
| format: ## Format code automatically | ||
| black $(BLACK_OPTS) | ||
|
|
||
| isort: ## Sort imports. This target is not mandatory because the output may be incompatible with black formatting. Provided for convenience purposes. | ||
| isort --skip=templates ${SRC_DIRS} | ||
|
|
||
| unittest: ## Run code tests cases | ||
| pytest tests | ||
|
|
||
| dev-requirements: ## Install dev requirements | ||
| pip install -e .[dev] | ||
|
|
||
| run-tests: test unittest # Run static analysis and unit tests | ||
|
|
||
| ESCAPE = | ||
| help: ## Print this help | ||
| @grep -E '^([a-zA-Z_-]+:.*?## .*|######* .+)$$' Makefile \ | ||
| | sed 's/######* \(.*\)/@ $(ESCAPE)[1;31m\1$(ESCAPE)[0m/g' | tr '@' '\n' \ | ||
| | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[33m%-30s\033[0m %s\n", $$1, $$2}' |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| Paragon plugin for `Tutor <https://docs.tutor.edly.io>`__ | ||
| ########################################################### | ||
|
|
||
| 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/>`__. | ||
|
|
||
| This plugin provides a local folder structure to manage **design token-based theme source files** (see `Paragon Design Tokens <https://github.com/openedx/paragon/?tab=readme-ov-file#design-tokens>`__) and compile them into CSS, enabling flexible customization of Open edX MFEs via Tutor. | ||
|
|
||
| Installation | ||
| ************ | ||
|
|
||
| .. code-block:: bash | ||
|
|
||
| pip install git+https://github.com/openedx/openedx-tutor-plugins.git#subdirectory=plugins/tutor-contrib-paragon | ||
|
|
||
| For development: | ||
|
|
||
| .. code-block:: bash | ||
|
|
||
| cd openedx-tutor-plugins/plugins/tutor-contrib-paragon | ||
| pip install -e . | ||
|
|
||
| Enable the plugin: | ||
|
|
||
| .. code-block:: bash | ||
|
|
||
| tutor plugins enable paragon | ||
|
|
||
| Directory Structure | ||
| ******************* | ||
|
|
||
| The plugin will create the following structure inside your Tutor environment: | ||
|
|
||
| .. code-block:: | ||
|
|
||
| tutor/env/plugins/paragon/ | ||
| βββ theme-sources/ # Place your Paragon-based theme folders here (e.g., theme-xyz/) | ||
| βββ compiled-themes/ # Output CSS files are generated here and ready for static hosting | ||
|
|
||
| Only themes listed in `PARAGON_ENABLED_THEMES` will be compiled. | ||
|
|
||
| Themes placed in `theme-sources/` are compiled into CSS using `Paragon's theme build process <https://github.com/openedx/paragon/?tab=readme-ov-file#paragon-cli>`_. The resulting CSS files in `compiled-themes/` are intended to be served statically and can be linked using the `PARAGON_THEME_URLS` setting. | ||
|
|
||
| This structure is optimized for design tokenβbased themes (see `Paragon Design Tokens <https://github.com/openedx/paragon/?tab=readme-ov-file#design-tokens>`__), but it is also flexible. If site operators need to include small amounts of additional CSS (not handled via tokens), we recommend doing so via extensions in the theme source directory, so they are included during the Paragon buildβrather than manually editing the compiled output. | ||
|
|
||
| .. note:: | ||
|
|
||
| Documentation for how to use extensions is not yet available. | ||
| A link to the official Open edX or Paragon documentation will be added here once it is published. | ||
|
|
||
| Configuration | ||
| ************* | ||
|
|
||
| All configuration variables can be overridden via `tutor config save`: | ||
|
|
||
| .. code-block:: yaml | ||
|
|
||
| PARAGON_THEME_SOURCES_PATH: "env/plugins/paragon/theme-sources" | ||
| PARAGON_COMPILED_THEMES_PATH: "env/plugins/paragon/compiled-themes" | ||
| PARAGON_ENABLED_THEMES: | ||
| - theme-1 | ||
| - theme-2 | ||
| PARAGON_SERVE_COMPILED_THEMES: true | ||
|
|
||
| You may customize paths or theme names to suit your deployment. | ||
|
|
||
| License | ||
| ******* | ||
|
|
||
| This software is licensed under the terms of the AGPLv3. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| # https://packaging.python.org/en/latest/tutorials/packaging-projects/ | ||
| # https://hatch.pypa.io/latest/config/build/ | ||
|
|
||
| [project] | ||
| name = "tutor-contrib-paragon" | ||
| description = "Facilitates the generation and static hosting of Paragon-based theme CSS files for Open edX MFEs using Tutor." | ||
| authors = [ | ||
| { name = "Alejandro Cardenas"}, | ||
| { email = "[email protected]" }, | ||
| ] | ||
| license = { text = "AGPL-3.0-only" } | ||
|
|
||
| readme = {file = "README.rst", content-type = "text/x-rst"} | ||
| requires-python = ">= 3.9" | ||
| classifiers = [ | ||
| "Development Status :: 3 - Alpha", | ||
| "Intended Audience :: Developers", | ||
| "License :: OSI Approved :: GNU Affero General Public License v3", | ||
| "Operating System :: OS Independent", | ||
| "Programming Language :: Python", | ||
| "Programming Language :: Python :: 3.9", | ||
| "Programming Language :: Python :: 3.10", | ||
| "Programming Language :: Python :: 3.11", | ||
| "Programming Language :: Python :: 3.12", | ||
| "Programming Language :: Python :: Implementation :: CPython", | ||
| "Programming Language :: Python :: Implementation :: PyPy", | ||
|
|
||
| ] | ||
| dependencies = [ | ||
| "tutor>=19.0.0,<20.0.0", | ||
| ] | ||
|
|
||
| optional-dependencies = { dev = ["tutor[dev]>=19.0.0,<20.0.0", "pytest>=8.3.4"] } | ||
|
|
||
| # These fields will be set by hatch_build.py | ||
| dynamic = ["version"] | ||
|
|
||
| # https://packaging.python.org/en/latest/specifications/well-known-project-urls/#well-known-labels | ||
| [project.urls] | ||
| Documentation = "https://github.com/openedx/openedx-tutor-plugins.git#subdirectory=plugins/tutor-contrib-paragon#readme" | ||
| Issues = "https://github.com/openedx/openedx-tutor-plugins.git#subdirectory=plugins/tutor-contrib-paragon/issues" | ||
| Source = "https://github.com/openedx/openedx-tutor-plugins.git#subdirectory=plugins/tutor-contrib-paragon" | ||
|
|
||
| [build-system] | ||
| requires = ["hatchling"] | ||
| build-backend = "hatchling.build" | ||
|
|
||
| # hatch-specific configuration | ||
| [tool.hatch.metadata.hooks.custom] | ||
| path = ".hatch_build.py" | ||
|
|
||
| [tool.hatch.build.targets.wheel] | ||
| packages = ["tutorparagon"] | ||
|
|
||
| [tool.hatch.build.targets.sdist] | ||
| # Disable strict naming, otherwise twine is not able to detect name/version | ||
| strict-naming = false | ||
| include = [ "/tutorparagon", ".hatch_build.py"] | ||
| exclude = ["tests*"] | ||
|
|
||
| [project.entry-points."tutor.plugin.v1"] | ||
| paragon = "tutorparagon.plugin" |
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| """ | ||
| Tutor paragon plugin tests | ||
| """ | ||
|
|
||
| from tutorparagon import __about__ | ||
|
|
||
| def test_version_exists(): | ||
| assert hasattr(__about__, "__version__") | ||
| assert isinstance(__about__.__version__, str) | ||
| assert __about__.__version__ != "" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| __version__ = "0.1.0" |
Empty file.
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| import os | ||
| from glob import glob | ||
|
|
||
| import click | ||
| import importlib_resources | ||
| from tutor import hooks | ||
| from tutor import config as tutor_config | ||
|
|
||
| from .__about__ import __version__ | ||
|
|
||
| ######################################## | ||
| # CONFIGURATION | ||
| ######################################## | ||
|
|
||
| hooks.Filters.CONFIG_DEFAULTS.add_items( | ||
| [ | ||
| # Plugin version (used for compatibility or debugging) | ||
| ("PARAGON_VERSION", __version__), | ||
| # Directory where users will place style-dictionary source files | ||
| # Each subfolder inside will represent a theme (e.g., "theme-abc/") | ||
| ("PARAGON_THEME_SOURCES_PATH", "env/plugins/paragon/theme-sources"), | ||
| # Directory where compiled CSS themes will be stored after transformation | ||
| # One subfolder per compiled theme (e.g., "theme-abc/core.min.css") | ||
| ("PARAGON_COMPILED_THEMES_PATH", "env/plugins/paragon/compiled-themes"), | ||
| # List of enabled themes to compile and serve | ||
| # Only themes listed here will be processed, even if others exist in sources | ||
| ("PARAGON_ENABLED_THEMES", []), | ||
| # Whether Tutor should expose the compiled themes to be served (e.g. via nginx, cady or static server) | ||
| ("PARAGON_SERVE_COMPILED_THEMES", True), | ||
| ] | ||
| ) | ||
|
|
||
|
|
||
| # Create directories for build and host | ||
| @hooks.Actions.PROJECT_ROOT_READY.add() | ||
| def create_paragon_folders(project_root: str) -> None: | ||
| config = tutor_config.load(project_root) | ||
|
|
||
| # Paths from config (always have defaults) | ||
| theme_sources_path = os.path.join( | ||
| project_root, str(config["PARAGON_THEME_SOURCES_PATH"]) | ||
| ) | ||
| compiled_themes_path = os.path.join( | ||
| project_root, str(config["PARAGON_COMPILED_THEMES_PATH"]) | ||
| ) | ||
|
|
||
| for path, label in [ | ||
| (theme_sources_path, "Theme Sources"), | ||
| (compiled_themes_path, "Compiled Themes"), | ||
| ]: | ||
| if os.path.exists(path): | ||
| print(f"[paragon] {label} folder already exists at: {path}") | ||
| else: | ||
| os.makedirs(path, exist_ok=True) | ||
| print(f"[paragon] Created {label} folder at: {path}") | ||
|
|
||
|
|
||
| ######################################## | ||
| # TEMPLATE RENDERING | ||
| # (It is safe & recommended to leave | ||
| # this section as-is :) | ||
| ######################################## | ||
|
|
||
| hooks.Filters.ENV_TEMPLATE_ROOTS.add_items( | ||
| # Root paths for template files, relative to the project root. | ||
| [ | ||
| str(importlib_resources.files("tutorparagon") / "templates"), | ||
| ] | ||
| ) | ||
|
|
||
| hooks.Filters.ENV_TEMPLATE_TARGETS.add_items( | ||
| # For each pair (source_path, destination_path): | ||
| # templates at ``source_path`` (relative to your ENV_TEMPLATE_ROOTS) will be | ||
| # rendered to ``source_path/destination_path`` (relative to your Tutor environment). | ||
| # For example, ``tutorparagon/templates/paragon/build`` | ||
| # will be rendered to ``$(tutor config printroot)/env/plugins/paragon/build``. | ||
| [ | ||
| ("paragon/build", "plugins"), | ||
| ("paragon/apps", "plugins"), | ||
| ], | ||
| ) | ||
|
|
||
|
|
||
| ######################################## | ||
| # PATCH LOADING | ||
| # (It is safe & recommended to leave | ||
| # this section as-is :) | ||
| ######################################## | ||
|
|
||
| # For each file in tutorparagon/patches, | ||
| # apply a patch based on the file's name and contents. | ||
| for path in glob(str(importlib_resources.files("tutorparagon") / "patches" / "*")): | ||
| with open(path, encoding="utf-8") as patch_file: | ||
| hooks.Filters.ENV_PATCHES.add_item((os.path.basename(path), patch_file.read())) |
Empty file.
Empty file.
Empty file.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It'd be great to link out to documentation on how to use extensions. I didn't see anything about that mentioned in openedx/brand-openedx#26 or https://github.com/openedx/paragon/?tab=readme-ov-file#design-tokens, so this can wait until after those docs are written.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great point! Iβll add a link to the docs once theyβre published. For now Iβll leave the mention as a placeholder.