-
Notifications
You must be signed in to change notification settings - Fork 101
[WIP] fix: add script to install node_modules in mounted mfes #232
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
base: release
Are you sure you want to change the base?
[WIP] fix: add script to install node_modules in mounted mfes #232
Conversation
tutormfe/plugin.py
Outdated
| def _run_jobs_in_mounted_mfes(config: Config) -> None: | ||
| mounts = get_typed(config, "MOUNTS", list, []) | ||
|
|
||
| pattern = re.compile(r'frontend-app-(\w+)') |
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.
We can use the REPO_PREFIX variable defined above here instead of a hardcoded string.
| @tutor_hooks.Actions.CONFIG_LOADED.add() | ||
| def _run_jobs_in_mounted_mfes(config: Config) -> None: | ||
| mounts = get_typed(config, "MOUNTS", list, []) | ||
|
|
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.
We can return here if no mounts exist in the config.
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.
+1
| @@ -0,0 +1,11 @@ | |||
| {%- for app_name, app in iter_mfes() %} | |||
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.
why is this patch needed?
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.
This patch is required to add the necessary jobs to run the init jobs inside the containers of the MFEs.
tutormfe/plugin.py
Outdated
| pattern = re.compile(r'frontend-app-(\w+)') | ||
| mounted_mfes = [match.group(1) for mount in mounts if (match := pattern.search(mount))] | ||
|
|
||
| mfe_task_file = os.path.join( |
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.
mfe_npm_install could be a better name as this action is related only to npm install.
| sha1sum "$PACKAGE_LOCK" | awk '{print $1}' | ||
| } | ||
|
|
||
| # Check if node_modules exists and fingerprint is valid |
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.
this comment can be broken down and mentioned against the appropriate if-elif-else block.
|
|
||
|
|
||
| @tutor_hooks.Actions.CONFIG_LOADED.add() | ||
| def _run_jobs_in_mounted_mfes(config: Config) -> None: |
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.
I'm not a big fan of adding callbacks to the CONFIG_LOADED action. It means we have to manually parse MOUNTS and this is bound to fail in many edge cases. Also, we have to start using Context objects, which is VERY dirty.
Instead, I suggest the following:
@tutor_hooks.Filters.CLI_DO_INIT_TASKS.add()
def _mfe_npm_install(tasks):
for mfe in get_mfes():
if hooks.Filters.COMPOSE_MOUNTS.apply([], f"frontend-app-{mfe}"):
tasks.append((mfe, "... contents of npm-install.sh..."))
What do you think?
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.
At first glance I agree with @regisb, here. If there are no technical blockers to doing it this way, I think it would be less problematic than adding a -jobs Docker service that needs to be duplicated in both dev and local compose files.
We're already assuming frontend-app-* in REPO_PREFIX anyway, and using it in different mount-related places. No harm in re-using it.
|
|
||
| # Store the new fingerprint after installation | ||
| generate_fingerprint > "$FINGERPRINT_FILE" | ||
| echo "✅ Dependencies installed and fingerprint updated." No newline at end of file |
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.
nit: add extra line at end of file
| - {{ mount }} | ||
| {%- endfor %} | ||
| {%- endif %} | ||
| {%- endfor %} No newline at end of file |
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.
nit: add extra line at end of file
| @@ -0,0 +1,11 @@ | |||
| {%- for app_name, app in iter_mfes() %} | |||
| {%- set mounts = iter_mounts(MOUNTS, app_name)|list %} | |||
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.
We moved from iter_mounts to a cleaner MFEMountData object in #241. We should use the similar format here for consistency.
| @@ -0,0 +1,7 @@ | |||
| {%- for app_name, app in iter_mfes() %} | |||
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.
Why is this patch necessary? Don't we just need to run the init job in dev?
tutormfe/plugin.py
Outdated
| return None | ||
|
|
||
| pattern = re.compile(rf'{re.escape(REPO_PREFIX)}(\w+)') | ||
| mounted_mfes = [match.group(1) for mount in mounts if (match := pattern.search(mount))] |
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.
Looking back, we should be able to use MFEMountData here as well instead of using a regex. What do you think?
49bb54f to
8b819e6
Compare
| # We only need to define these for local as well otherwise docker throws a not found error when we run the local init jobs | ||
| # These jobs are required to run the npm install script when mounting MFEs | ||
| {%- set mfe_data = MFEMountData(MOUNTS) %} | ||
|
|
||
| {%- for app_name, app, mounts in mfe_data.mounted %} | ||
| {{ app_name }}-job: | ||
| image: "{{ MFE_DOCKER_IMAGE_DEV_PREFIX }}-{{ app_name }}-dev:{{ MFE_VERSION }}" | ||
| volumes: | ||
| {%- for mount in mounts %} | ||
| - {{ mount }} | ||
| {%- endfor %} | ||
| environment: | ||
| - "NODE_ENV=production" | ||
| {%- endfor %} |
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.
I dont understand why this is needed. It is using dev prefixes in local/production env, which in itself is not coherent. Why is local init job failing if this is not provided?
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.
We add a job for every mounted MFE to the CLI_DO_INIT_TASKS filter. When we run the initialization task with the local context, aka tutor local do init, it will try to run those jobs that we added. However, we never defined a docker compose service to run that job in, therefore it fails.
We define this job structure here to let docker know where to run the job for each MFE. The job will still run in local mode but will exit due to the NODE_ENV environment variable being set to production.
The reason we added dev prefixes for local is just for consistency as we need to task to exit after discovering that NODE_ENV is set to production.
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.
We add a job for every mounted MFE to the CLI_DO_INIT_TASKS filter. When we run the initialization task with the local context, aka tutor local do init, it will try to run those jobs that we added. However, we never defined a docker compose service to run that job in, therefore it fails.
Let's only do this for dev context. It is confusing to have this for local when it is not meant to be used.
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.
Right, we tried to find a way to do that, however we are using the CONFIG_LOADED action to parse mounts and add our jobs. This action does not provide us any information if the job is being run with the local or dev context and therefore, we can't add it conditionally.
The only hook that provides this information is the APP_PUBLIC_HOSTS filter and we cannot use it as it doesn't provide us information regarding mounts. This is why we had no choice but to run the job in both local and dev containers and then exit if a production ENV is discovered.
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.
Ah, ok.
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.
@arbrandes what do you think about this?
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.
@arbrandes Ping 🙂
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.
Have you given @regisb's suggestion a try? I too feel that having this in the local file is troublesome, and doing it some other way (i.e., a DO_INIT hook that iterates over COMPOSE_MOUNTS) looks like it would not require adding a -jobs service.
arbrandes
left a comment
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.
Before moving forward, I'd like to understand if a less invasive alternative (@regisb's suggestion, specifically) was investigated.
| # We only need to define these for local as well otherwise docker throws a not found error when we run the local init jobs | ||
| # These jobs are required to run the npm install script when mounting MFEs | ||
| {%- set mfe_data = MFEMountData(MOUNTS) %} | ||
|
|
||
| {%- for app_name, app, mounts in mfe_data.mounted %} | ||
| {{ app_name }}-job: | ||
| image: "{{ MFE_DOCKER_IMAGE_DEV_PREFIX }}-{{ app_name }}-dev:{{ MFE_VERSION }}" | ||
| volumes: | ||
| {%- for mount in mounts %} | ||
| - {{ mount }} | ||
| {%- endfor %} | ||
| environment: | ||
| - "NODE_ENV=production" | ||
| {%- endfor %} |
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.
Have you given @regisb's suggestion a try? I too feel that having this in the local file is troublesome, and doing it some other way (i.e., a DO_INIT hook that iterates over COMPOSE_MOUNTS) looks like it would not require adding a -jobs service.
|
|
||
|
|
||
| @tutor_hooks.Actions.CONFIG_LOADED.add() | ||
| def _run_jobs_in_mounted_mfes(config: Config) -> None: |
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.
At first glance I agree with @regisb, here. If there are no technical blockers to doing it this way, I think it would be less problematic than adding a -jobs Docker service that needs to be duplicated in both dev and local compose files.
We're already assuming frontend-app-* in REPO_PREFIX anyway, and using it in different mount-related places. No harm in re-using it.
WIP -> The jobs run on tutor dev only
Description
These code changes introduce improvements to the Tutor MFE (Micro-Frontend) plugin by adding automation and efficiency for managing and running tasks in environments where MFEs are mounted. When MFEs are mounted, it will run
npm clean-installas part of the runtutor dev launch. The "mfe" init tasknpm-install.shis taking care of installing the node_modules, as it is done in the LMSTest Cases:
npm-installscript when MFEs are implicitly and expicitly mounted using the Tutor format:tutor mounts add $(pwd)/frontend-app-*&tutor mounts add [mfe-name]:$(pwd)/frontend-app-*:/openedx/app.tutor dev, nottutor local, as MFEs mount only intutor devenvironment.node_modulesare present, it should installnode_modules.node_modulesdirectory exists but is empty or outdated, it should installnode_modules.package-lock.jsonfile changes slightly, it should trigger reinstallation ofnode_modules.node_modulesare installed and then reinstallation is attempted without changes topackage-lock.json, it should not reinstallnode_modules.Closes #218
Special thanks to @Danyal-Faheem for collaborating with me on this and helping to reach a solution. 🙌