Skip to content

Commit 70486f6

Browse files
committed
feat: add Paragon build-tokens command using tutor custom jobs
1 parent bc0ab92 commit 70486f6

File tree

4 files changed

+154
-0
lines changed

4 files changed

+154
-0
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Paragon Builder
2+
paragon-builder-job:
3+
image: {{ PARAGON_BUILDER_IMAGE }}
4+
environment:
5+
- PARAGON_ENABLED_THEMES={{ PARAGON_ENABLED_THEMES | join(',') }}
6+
volumes:
7+
- "./../../{{ PARAGON_THEME_SOURCES_PATH }}:/theme-sources"
8+
- "./../../{{ PARAGON_COMPILED_THEMES_PATH }}:/compiled-themes"

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

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
("PARAGON_ENABLED_THEMES", []),
2828
# Whether Tutor should expose the compiled themes to be served (e.g. via nginx, cady or static server)
2929
("PARAGON_SERVE_COMPILED_THEMES", True),
30+
# Paragon Builder Docker image
31+
# This image is used to compile themes and should be built with `tutor images build paragon-builder`
32+
("PARAGON_BUILDER_IMAGE", "paragon-builder:latest"),
3033
]
3134
)
3235

@@ -55,6 +58,22 @@ def create_paragon_folders(project_root: str) -> None:
5558
print(f"[paragon] Created {label} folder at: {path}")
5659

5760

61+
########################################
62+
# DOCKER IMAGE MANAGEMENT
63+
########################################
64+
65+
hooks.Filters.IMAGES_BUILD.add_items(
66+
[
67+
(
68+
"paragon-builder", # Image name used with 'tutor images build myservice'
69+
("plugins", "paragon", "build", "paragon-builder"), # Path to Dockerfile
70+
"{{ PARAGON_BUILDER_IMAGE }}", # Docker image tag
71+
(), # Build arguments
72+
),
73+
]
74+
)
75+
76+
5877
########################################
5978
# TEMPLATE RENDERING
6079
# (It is safe & recommended to leave
@@ -92,3 +111,60 @@ def create_paragon_folders(project_root: str) -> None:
92111
for path in glob(str(importlib_resources.files("tutorparagon") / "patches" / "*")):
93112
with open(path, encoding="utf-8") as patch_file:
94113
hooks.Filters.ENV_PATCHES.add_item((os.path.basename(path), patch_file.read()))
114+
115+
116+
########################################
117+
# CUSTOM JOBS (a.k.a. "do-commands")
118+
########################################
119+
120+
121+
@click.command()
122+
@click.option(
123+
"--source-tokens-only",
124+
is_flag=True,
125+
default=False,
126+
help="Include only source design tokens in the build.",
127+
)
128+
@click.option(
129+
"--output-token-references",
130+
is_flag=True,
131+
default=False,
132+
help="Include references for tokens with aliases to other tokens in the build output.",
133+
)
134+
@click.option("--themes", help="Comma-separated list of themes to build.")
135+
@click.option(
136+
"-v", "--verbose", is_flag=True, default=False, help="Enable verbose logging."
137+
)
138+
def build_tokens(
139+
themes: str,
140+
source_tokens_only: bool,
141+
output_token_references: bool,
142+
verbose: bool,
143+
) -> list[tuple[str, str]]:
144+
"""
145+
Build theme token files using Paragon.
146+
147+
Args:
148+
source_tokens_only (bool): Only source design tokens.
149+
output_token_references (bool): Output token references.
150+
themes (str): Comma-separated list of themes.
151+
verbose (bool): Verbose logging.
152+
153+
Returns:
154+
list[tuple[str, str]]: List of commands to run.
155+
"""
156+
args = []
157+
if source_tokens_only:
158+
args.append("--source-tokens-only")
159+
if output_token_references:
160+
args.append("--output-token-references")
161+
if themes:
162+
args.append("--themes")
163+
args.append(themes)
164+
if verbose:
165+
args.append("--verbose")
166+
167+
return [("paragon-builder", " ".join(args))]
168+
169+
170+
hooks.Filters.CLI_DO_COMMANDS.add_item(build_tokens)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
FROM node:18-alpine
2+
3+
COPY entrypoint.sh /entrypoint.sh
4+
5+
RUN npm install --save @openedx/paragon && \
6+
chmod +x /entrypoint.sh
7+
8+
ENTRYPOINT ["sh", "/entrypoint.sh"]
9+
10+
CMD []
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#!/bin/sh
2+
set -e
3+
4+
SOURCE_DIR="/theme-sources" # Volume mounted by Paragon plugin
5+
FINAL_BUILD_DIR="/compiled-themes" # Volume mounted by Paragon plugin
6+
TMP_BUILD_DIR="$(mktemp -d /tmp/paragon-build.XXXXXX)"
7+
8+
9+
has_themes() {
10+
# This function checks if the --themes flag is present in the arguments
11+
# Returns 0 if --themes is found, 1 otherwise
12+
# Usage: has_themes "$@"
13+
14+
for arg in "$@"; do
15+
[ "$arg" = "--themes" ] && return 0
16+
done
17+
return 1
18+
}
19+
20+
parse_args() {
21+
# This function parses arguments injected by Tutor.
22+
# Tutor runs jobs via `sh -e -c '…'`, reserving $1–$3 for its wrapper and
23+
# bundling all user flags into $4. The function then extracts the raw flags
24+
# string from $4, shifts off the wrapper args, and verifies—or appends—the
25+
# selected themes definition.
26+
27+
if [ "$#" -lt 4 ]; then
28+
echo "Error: Expected at least 4 args, got $#. Must run via tutor."
29+
exit 1
30+
fi
31+
32+
shift 3
33+
34+
if ! has_themes "$@"; then
35+
if [ -n "${PARAGON_ENABLED_THEMES:-}" ]; then
36+
echo "Using PARAGON_ENABLED_THEMES: ${PARAGON_ENABLED_THEMES}"
37+
set -- "$@" --themes "${PARAGON_ENABLED_THEMES}"
38+
fi
39+
fi
40+
41+
printf '%s\n' "$@"
42+
}
43+
44+
set -- $(parse_args "$@")
45+
46+
# Executes the Paragon CLI to build themes.
47+
# It uses a temporary directory to avoid volume permissions issues
48+
npx paragon build-tokens \
49+
--source "$SOURCE_DIR" \
50+
--build-dir "$TMP_BUILD_DIR" \
51+
"$@"
52+
53+
# Moves the built themes to the final volume directory.
54+
mkdir -p "$FINAL_BUILD_DIR"
55+
cp -a "$TMP_BUILD_DIR/." "$FINAL_BUILD_DIR/"
56+
57+
# Clean up
58+
rm -rf "$TMP_BUILD_DIR"
59+
60+
exit 0

0 commit comments

Comments
 (0)