Goal #1: Develop a fix for frontend-platform using Tutor

I’m trying to make a small fix in frontend-platform and test it using the frontend-app-library-authoring MFE in tutor dev.

This is my personal log of Tutor and MFE things I’m learning along the way.

Things that confused me…

Why doesn’t mounts specify an image/container?

The MOUNTS in my tutor config looks like this:

MOUNTS:
- /Users/daveormsbee/Projects/openedx/edx-platform
- /Users/daveormsbee/Projects/openedx/frontend-app-library-authoring

With the implicit form, plugins are in charge of automatically detecting in which containers and locations the /host/path folder should be bind-mounted. In this case, folders can be bind-mounted at build-time – which cannot be achieved with the explicit form. (from tutor docs)

So tutor-mfe plugin does some magic introspection based on the name of the repo directory:

@tutor_hooks.Filters.COMPOSE_MOUNTS.add()
def _mount_frontend_apps(volumes, path_basename):
    """
    If the user mounts any repo named frontend-app-APPNAME, then make sure
    it's available in the APPNAME service container. This is only applicable
    in dev mode, because in production, all MFEs are built and hosted on the
    singular 'mfe' service container.
    """
    if path_basename.startswith(REPO_PREFIX):
        # Assumption:
        # For each repo named frontend-app-APPNAME, there is an associated
        # docker-compose service named APPNAME. If this assumption is broken,
        # then Tutor will try to mount the repo in a service that doesn't exist.
        app_name = path_basename[len(REPO_PREFIX) :]
        volumes += [(app_name, "/openedx/app")]
    return volumes

You have to restart the container after running tutor mounts

I ran the following command to mount my local copy of frontend-platform to the library-authoring service:

tutor mounts add library-authoring:/Users/daveormsbee/Projects/openedx/frontend-platform:/openedx/frontend-platform

When I run tutor mounts list, this is what comes out:

❯ tutor mounts list
- name: /Users/daveormsbee/Projects/openedx/edx-platform
  build_mounts:
  - image: openedx
    context: edx-platform
  - image: openedx-dev
    context: edx-platform
  compose_mounts:
  - service: lms
    container_path: /openedx/edx-platform
  - service: cms
    container_path: /openedx/edx-platform
  - service: lms-worker
    container_path: /openedx/edx-platform
  - service: cms-worker
    container_path: /openedx/edx-platform
  - service: lms-job
    container_path: /openedx/edx-platform
  - service: cms-job
    container_path: /openedx/edx-platform
- name: /Users/daveormsbee/Projects/openedx/frontend-app-library-authoring
  build_mounts:
  - image: mfe
    context: library-authoring-src
  - image: library-authoring-dev
    context: library-authoring-src
  compose_mounts:
  - service: library-authoring
    container_path: /openedx/app
- name: library-authoring:/Users/daveormsbee/Projects/openedx/frontend-platform:/openedx/frontend-platform
  build_mounts: []
  compose_mounts:
  - service: library-authoring
    container_path: /openedx/frontend-platform

It took me a bit to realize that mounts don’t happen immediately, and that I needed to restart the library-authoring service in order to see it mounted.

Side note: I chose to mount it in /openedx/frontend-platform because it should make it so that a relative link from frontend-app-library-authoring should stay the same on host and in tutor (on the image, frontend-app-library-authoring is mounted into /openedx/app. So as long as both repos are checked out next to each other on the host, a link to "../frontend-platform" should work.

Local paths didn’t seem to work in module.config.js

The example module.config.js that frontend-build gives shows local, relative paths. I wanted to just override frontend-platform, so I used this:

module.exports = {
  /*
  Modules you want to use from local source code.  Adding a module here means that when this app
  runs its build, it'll resolve the source from peer directories of this app.

  moduleName: the name you use to import code from the module.
  dir: The relative path to the module's source code.
  dist: The sub-directory of the source code where it puts its build artifact.  Often "dist".
  */
  localModules: [
    { moduleName: '@edx/frontend-platform', dir: '../src/frontend-platform', dist: 'dist' },
  ],
};

This didn’t work for me:

frontend-app-library-authoring (44195fb) via 🐍 v3.8.12 (tutor) is 📦 v0.1.0
❯ npm install && npm run build

up to date, audited 1917 packages in 1s

239 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

> @edx/frontend-app-library-authoring@0.1.0 build
> fedx-scripts webpack

Running with resolved config:
/Users/daveormsbee/Projects/openedx/frontend-app-library-authoring/webpack.prod.config.js

Resolving modules from local directories via module.config.js.
Using local version of @edx/frontend-platform from ../src/frontend-platform/dist.
Error: Cannot find module '/Users/daveormsbee/Projects/openedx/src/frontend-platform/package.json'
Require stack:
- /Users/daveormsbee/Projects/openedx/frontend-app-library-authoring/node_modules/@edx/frontend-build/config/getLocalAliases.js
- /Users/daveormsbee/Projects/openedx/frontend-app-library-authoring/node_modules/@edx/frontend-build/config/webpack.dev.config.js
- /Users/daveormsbee/Projects/openedx/frontend-app-library-authoring/node_modules/@edx/frontend-build/lib/ConfigPreset.js
- /Users/daveormsbee/Projects/openedx/frontend-app-library-authoring/node_modules/@edx/frontend-build/lib/presets.js
- /Users/daveormsbee/Projects/openedx/frontend-app-library-authoring/node_modules/@edx/frontend-build/bin/fedx-scripts.js
    at Module._resolveFilename (node:internal/modules/cjs/loader:1077:15)
    at Module._load (node:internal/modules/cjs/loader:922:27)
    at Module.require (node:internal/modules/cjs/loader:1143:19)
    at require (node:internal/modules/cjs/helpers:110:18)
    at /Users/daveormsbee/Projects/openedx/frontend-app-library-authoring/node_modules/@edx/frontend-build/config/getLocalAliases.js:48:47
    at Array.forEach (<anonymous>)
    at getLocalAliases (/Users/daveormsbee/Projects/openedx/frontend-app-library-authoring/node_modules/@edx/frontend-build/config/getLocalAliases.js:45:18)
    at Object.<anonymous> (/Users/daveormsbee/Projects/openedx/frontend-app-library-authoring/node_modules/@edx/frontend-build/config/webpack.dev.config.js:31:17)
    at Module._compile (node:internal/modules/cjs/loader:1256:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1310:10) {
  code: 'MODULE_NOT_FOUND',
  requireStack: [
    '/Users/daveormsbee/Projects/openedx/frontend-app-library-authoring/node_modules/@edx/frontend-build/config/getLocalAliases.js',
    '/Users/daveormsbee/Projects/openedx/frontend-app-library-authoring/node_modules/@edx/frontend-build/config/webpack.dev.config.js',
    '/Users/daveormsbee/Projects/openedx/frontend-app-library-authoring/node_modules/@edx/frontend-build/lib/ConfigPreset.js',
    '/Users/daveormsbee/Projects/openedx/frontend-app-library-authoring/node_modules/@edx/frontend-build/lib/presets.js',
    '/Users/daveormsbee/Projects/openedx/frontend-app-library-authoring/node_modules/@edx/frontend-build/bin/fedx-scripts.js'
  ]
}
Error in module.config.js parsing. module.config.js will be ignored.
<w> [ReactRefreshPlugin] Hot Module Replacement (HMR) is not enabled! React Refresh requires HMR to function properly.
assets by status 27.8 KiB [cached] 8 assets
assets by path . 62.3 MiB
  asset app.js 62.3 MiB [emitted] (name: app)
  asset xblock-bootstrap.html 2.55 KiB [compared for emit] [from: src/library-authoring/edit-block/LibraryBlock/xblock-bootstrap.html] [copied]
  asset index.html 433 bytes [compared for emit]
5235 modules
webpack 5.88.2 compiled successfully in 11895 ms

This was because I copy-pasted without reading properly. The dir in the config should have been ../frontend-platform, but I had kept it ../src/frontend-platform

Got compile errors when trying to build frontend-app-library-authoring

The key here was to build frontend-platform before trying to build frontent-app-library-authoring.

VS Code’s Dev Containers Plugin AUTOMATICALLY started port forwarding

I was connecting to the CMS container, and didn’t realize that VS Code would automatically start port forwarding. This is a problem because Django in the CMS container runs at 8000, but is supposed to be connected to at 8001 from the outside. Port 8000 is reserved the for LMS. So what was happening was that VS Code was taking the port away from the LMS whenever the LMS code was reloaded. I was working on a Studio feature, so things continued to work fine for me until my auth token expired and the Library Authoring MFE errored because it was trying to hit an oauth2 endpoint that lives on the LMS, but was forwarded to CMS instead.

I haven’t seen a way to turn this off in the settings, but being able to connect to a container and get all the python code loaded for introspection is really nice. So I’ve taken to always connecting to the LMS container–VS Code still grabs and forwards the port, but it forwards it to the same place it was going before.