Feat/cross repo dispatch to lms#1921
Conversation
Drops in the workflow template from upstream Rooman LMS repo
(edx/ops/mfe/workflow.example.yml). On every push to rooman/main:
- Build the upstream Dockerfile (which the MFE already ships)
- Push to ghcr.io/punithrooman/rooman-frontend-app-learning with
both :latest and a :rooman-main-<short-sha> tag (reproducible
for prod deploys)
- GHA cache cuts subsequent builds from ~6 min to ~90s
After this lands, the LMS box can pull the resulting image by setting
MFE_LEARNING_DOCKER_IMAGE in ~/.local/share/tutor/config.yml.
Branch base: open-release/sumac.master (the latest stable Open edX
MFE release as of May 2026). When Verbena MFE release branches drop,
rebase rooman/main onto open-release/verbena.master.
Upstream `openedx/frontend-app-learning` doesn't ship a Dockerfile —
Tutor's mfe plugin builds the image itself using its own template.
For our fork we need a Dockerfile so the GHA workflow can build a
production image and push it to GHCR.
Two stages:
1. node:20-bookworm-slim builder
- apt-get the toolchain fedx-scripts needs (git, python3, build-essential)
- npm ci with patches/ pre-copied so patch-package runs in postinstall
- generate env.config.jsx from the upstream example if missing
(Tutor injects the real runtime config at container start, not here)
- npm run build → dist/
2. caddy:2-alpine server
- COPY --from=builder /app/dist → /usr/share/caddy
- Caddyfile with try_files fallback for SPA routes
- listen on :8080 (matches Tutor's expected MFE container port)
Tutor's MFE plugin injects runtime config (LMS_BASE_URL etc.) via
env vars + a generated /static/env.config.js at container start;
this image deliberately doesn't bake anything host-specific in so
the same image works in dev + prod.
Build locally with: `docker build -t test .` (takes ~6 min cold,
~90s cached).
env.config.jsx is gitignored in the upstream MFE — it's meant to be
per-deployment local config, not committed. So my last attempt (a
checked-in env.config.jsx) couldn't be staged.
Better: generate the minimal file inline in the Dockerfile via a
heredoc. The file ends up the same — exposes process.env + an empty
pluginSlots map — but the build remains self-contained and doesn't
fight upstream's .gitignore.
When Rooman adds custom plugin slots (AI tutor sidebar etc.),
replace the heredoc with a checked-in file at a non-ignored path:
config/env.config.jsx # added to fork, not gitignored
COPY config/env.config.jsx ./ # before npm run build
Until then the heredoc is the simplest path.
Adds a new sidebar (alongside Discussions + Notifications) that gives
the learner an in-context chat with the Rooman tutor backend. Wired
to POST /api/practice/tutor/chat on the lab platform.
What learners see
─────────────────
- New chat-bubble icon in the courseware sidebar trigger column,
topmost in the order.
- Click → 30rem-wide panel slides in from the right with the title
"Rooman AI Tutor".
- Empty state: "Ask anything about this lesson…"
- Type a question, press Enter (Shift+Enter for newline), get a reply
from the LLM via the lab platform.
- Multi-turn: history persists in component state for the session;
refresh wipes (deliberate for v0.1 — no per-learner server-side
history yet).
- On error: the learner's typed message is restored so they can edit
and retry without losing what they wrote.
Files added (all additive — no upstream-file edits except the sidebars
registry, which is intentionally a small, stable file that rarely
moves):
src/courseware/course/sidebar/sidebars/rooman-tutor/
├── index.js re-exports
├── messages.ts i18n strings
├── RoomanTutorTrigger.jsx sidebar trigger button
└── RoomanTutorSidebar.jsx the chat panel
src/courseware/course/sidebar/sidebars/index.js
Added roomanTutor entry to SIDEBARS map + SIDEBAR_ORDER.
Configuration
─────────────
Lab platform host comes from `process.env.LAB_PLATFORM_BASE_URL` with
a default of `https://dev-labs.13-232-120-92.sslip.io`. Override per
deployment via Tutor's MFE_CONFIG_OVERRIDES in config.yml:
MFE_CONFIG_OVERRIDES:
learning:
LAB_PLATFORM_BASE_URL: https://dev-labs.13-232-120-92.sslip.io
CORS: this sidebar issues a cross-origin POST from apps.dev-lms.<host>
to dev-labs.<host>. The lab platform's CORS middleware allows `*`,
but browsers reject the `*`-origin + `allow_credentials=true`
combination. We send `credentials: 'omit'` — the chat endpoint is
unauth so we don't need them.
Rebase discipline
─────────────────
Everything lives in a new sidebar directory (additive), with one
small touch to `sidebars/index.js` (the registry — stable file that
rarely moves upstream). Rebases of this work onto the next Open edX
release should be ~5 minutes.
…sets correctly Without this, webpack emits src="/runtime.xxx.js" (root-relative) but Tutor's MFE Caddyfile only handles /learning/* paths. Root-level JS requests return empty 200s, so React never mounts and the page is blank. With PUBLIC_URL=/learning/, webpack emits src="/learning/runtime.xxx.js" which the Caddyfile's @mfe_learning handler serves correctly.
…platform init Without APP_ID=learning, the config fetch goes to ?mfe=undefined and initialization fires APP_INIT_ERROR, showing the error boundary page. MFE_CONFIG_API_URL=/api/mfe_config/v1 is a relative URL so Caddy proxies it to the LMS. Both are set by Tutor's own MFE Dockerfile template.
|
Thanks for the pull request, @ChandanaSRooman! This repository is currently maintained by Once you've gone through the following steps feel free to tag them in a comment and let them know that your changes are ready for engineering review. 🔘 Get product approvalIf you haven't already, check this list to see if your contribution needs to go through the product review process.
🔘 Provide contextTo help your reviewers and other members of the community understand the purpose and larger context of your changes, feel free to add as much of the following information to the PR description as you can:
🔘 Submit a signed contributor agreement (CLA)
If you've signed an agreement in the past, you may need to re-sign. Once you've signed the CLA, please allow 1 business day for it to be processed. 🔘 Get a green buildIf one or more checks are failing, continue working on your changes until this is no longer the case and your build turns green. DetailsWhere can I find more information?If you'd like to get more details on all aspects of the review process for open source pull requests (OSPRs), check out the following resources: When can I expect my changes to be merged?Our goal is to get community contributions seen and reviewed as efficiently as possible. However, the amount of time that it takes to review and merge a PR can vary significantly based on factors such as:
💡 As a result it may take up to several weeks or months to complete a review and merge your PR. |
…push Adds a final step to build-and-push.yml that calls the GitHub Actions repository_dispatch API on ChandanaSRooman/LMS with event_type= mfe-source-updated. The LMS-side workflow build-mfe-bundle.yml listens for that event and rebuilds the unified MFE bundle (this MFE + 10 other MFEs) into one Docker image pushed to GHCR, then deploys to the dev EC2. Why: the LMS-side build needs to clone the MFE source at the exact sha that triggered the build (not pull an image), because Tutor's `tutor images build mfe` combines all 11 MFE source repos into one container. So we pass GITHUB_SHA along in the dispatch payload. The step only fires on push events (not workflow_dispatch re-runs) to prevent build loops. Requires a PAT secret `LMS_MONOREPO_DISPATCH_PAT` set on this repo with fine-grained access to ChandanaSRooman/LMS: Repository contents = read Actions = write Without the secret, the step emits a warning and exits 0, so the MFE image build still succeeds. (Renamed type from ci -> build to satisfy OEP-51 commitlint type-enum; upstream openedx commitlint config doesn't allow ci:.) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
17e38b0 to
f0255c3
Compare
No description provided.