Skip to content

DO NOT MERGE - DO NOT CLOSE - Dummy PR to track upstream master#824

Draft
kaustavb12 wants to merge 487 commits into
open-craft:kaustav/downstream_pr_targetfrom
openedx:master
Draft

DO NOT MERGE - DO NOT CLOSE - Dummy PR to track upstream master#824
kaustavb12 wants to merge 487 commits into
open-craft:kaustav/downstream_pr_targetfrom
openedx:master

Conversation

@kaustavb12

@kaustavb12 kaustavb12 commented Feb 6, 2026

Copy link
Copy Markdown
Member

Settings

AN_IMPORTANT_NOTICE: |
  ##########################################
  This is the OpenCraft Sandbox (sandbox.opencraft.com) tracking upstream master.
  Please do not delete or modify this instance without checking with Fox first.
  ##########################################
PLATFORM_NAME: OpenCraft Sandbox
LMS_HOST: sandbox.opencraft.com
CMS_HOST: studio.sandbox.opencraft.com
PREVIEW_LMS_HOST: preview.sandbox.opencraft.com
GROVE_NEW_MFES:
  catalog:
    port: 1998
    repository: https://github.com/openedx/frontend-app-catalog.git
    version: master
GROVE_SIMPLE_THEME_BRANCH: sandbox
GROVE_SIMPLE_THEME_REPO: https://github.com/open-craft/brand-openedx.git
GROVE_COMMON_SETTINGS: |
  CATALOG_MICROFRONTEND_URL = 'https://apps.sandbox.opencraft.com/catalog'
  ENABLE_CATALOG_MICROFRONTEND = True
  DEFAULT_COURSE_VISIBILITY_IN_CATALOG = 'none'
GROVE_MFE_LMS_COMMON_SETTINGS: |
  MFE_CONFIG['LOGO_URL'] = 'https://raw.githubusercontent.com/open-craft/brand-openedx/refs/heads/sandbox/logo.png'
  MFE_CONFIG['LOGO_TRADEMARK_URL'] = 'https://raw.githubusercontent.com/open-craft/brand-openedx/refs/heads/sandbox/logo-trademark.png'
  MFE_CONFIG['LOGO_WHITE_URL'] = 'https://raw.githubusercontent.com/open-craft/brand-openedx/refs/heads/sandbox/logo-white.png'
  MFE_CONFIG['FAVICON_URL'] = 'https://raw.githubusercontent.com/open-craft/brand-openedx/refs/heads/sandbox/favicon.ico'
  MFE_CONFIG_OVERRIDES['learner-dashboard'] = {'LOGO_URL': 'https://raw.githubusercontent.com/open-craft/brand-openedx/refs/heads/sandbox/logo-white.png'}
  MFE_CONFIG_OVERRIDES['catalog'] = {'LOGO_URL': 'https://raw.githubusercontent.com/open-craft/brand-openedx/refs/heads/sandbox/logo-white.png'}
  MFE_CONFIG_OVERRIDES['profile'] = {'LOGO_URL': 'https://raw.githubusercontent.com/open-craft/brand-openedx/refs/heads/sandbox/logo-white.png'}
  MFE_CONFIG_OVERRIDES['account'] = {'LOGO_URL': 'https://raw.githubusercontent.com/open-craft/brand-openedx/refs/heads/sandbox/logo-white.png'}
OPENEDX_EXTRA_PIP_REQUIREMENTS:
- git+https://gitlab.com/opencraft/dev/openedx-auto-studio.git@master
- git+https://github.com/open-craft/openedx-edit-links.git@main
- xblock-problem-builder
CONTACT_EMAIL: help@opencraft.com

Tutor requirements

tutor plugins enable sandbox
tutor plugins enable grove-simple-theme
tutor generate-tokens

@kaustavb12 kaustavb12 marked this pull request as draft February 6, 2026 08:34
@open-craft open-craft locked and limited conversation to collaborators Feb 10, 2026
@kaustavb12 kaustavb12 changed the title test: DO NOT MERGE - Dummy PR to track upstream master DO NOT MERGE - DO NOT DELETE - Dummy PR to track upstream master Apr 7, 2026
@kaustavb12 kaustavb12 changed the title DO NOT MERGE - DO NOT DELETE - Dummy PR to track upstream master DO NOT MERGE - DO NOT CLOSE - Dummy PR to track upstream master Apr 7, 2026
feanil and others added 25 commits April 11, 2026 17:09
Push this forward so things don't automatically in a few years for future courses.
…RT_DATE

Tests were asserting against the literal `2030-01-01` value instead of
importing the constant, causing failures after the default was updated to 2040.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Commit 3453e7d introduced a fix 
to synchronize DiscussionsConfiguration.enabled (DB) with tab.is_hidden
(modulestore) during course imports.

This commit improves upon that fix in two ways:

* It updates the discussion CourseAppStatus as well, which is
  necessary in order for the app to be fully enabled.
* It adds a data migration to perform the above synchronization
  retroactively for all existing courses, using the CourseOverTab
  table.
* feat: use openedx-core branch with strongly-typed keys
* chore: update to use strongly-typed IDs from openedx_content
* feat: fully typed primary keys for StagedContent model
* chore: misc typing improvements + type-check `helpers.py` in CMS
* chore: explain mypy error and suppress it for now
* chore: use .id instead of .pk
* fix: update legacy attribute key
fix: add weight from max_weight if its missing from meta
…ublished-signals

fix: fix multiple COURSE_PUBLISHED signals being fired when saving
…ubclasses

Three test classes in the certificates app were calling CourseFactory() in
setUp() despite extending SharedModuleStoreTestCase. Unlike ModuleStoreTestCase,
SharedModuleStoreTestCase shares a single modulestore across all tests in the
class and only closes MongoDB connections at tearDownClass. Calling
CourseFactory() in setUp() created a new MongoDB course (and opened connections)
for every test method without releasing them, causing connection accumulation
across the full test run.

Affected classes:
- CertificateFiltersTest (test_filters.py)
- CertificateInvalidationTest (test_models.py)
- CertificateAllowlistTest (test_models.py)

In each case the course is only read by test methods (test data such as users,
enrollments and certificates is written via Django ORM and rolled back between
tests), so sharing a single course across the class is correct.

See: https://github.com/openedx/openedx-platform/blob/master/xmodule/modulestore/tests/django_utils.py

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…vents.testing

openedx_events/tests/utils.py was moved to openedx_events/testing.py in
openedx/openedx-events#559 so the test utilities are included in the
installed package (setup.py excludes the tests/ subpackage from the wheel).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When OpenEdxEventsTestMixin was listed after a TestCase subclass (e.g.
Foo(SharedModuleStoreTestCase, OpenEdxEventsTestMixin)), it landed after
unittest.case.TestCase in the MRO. Since unittest.case.TestCase.setUpClass
and tearDownClass do not call super(), the mixin's lifecycle methods never
ran. The workaround was to manually call cls.start_events_isolation() in
each class's setUpClass, but there was no corresponding tearDownClass to
restore event state, causing events disabled by one test class to leak into
subsequent classes in the same process.

Fix by placing OpenEdxEventsTestMixin first in the base class list so it
appears before unittest.case.TestCase in the MRO. This lets setUpClass and
tearDownClass run automatically through the cooperative super() chain,
removing the need for manual start_events_isolation() calls.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This mixin is already included via one of the other mixins on this test
class so including it again was messing with the MRO for the test
classes.
Run pytest with extra reporting enabled to generate files with per-test
durations. The file is uploaded as a CI artifact so timing data can be
downloaded and used to drive optimal shard rebalancing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Redistribute test paths across 9 shards (down from 16) using a greedy
bin-packing optimiser driven by real per-test timing data from
pytest-reportlog. Predicted critical path: ~18.7m (down from ~29m).

Key changes:
- Rename shard groups to reflect semantic meaning: lms-*, shared-with-lms-*,
  shared-with-cms-*, cms-* (openedx/common/xmodule paths explicitly separated
  from lms-only and cms-only paths)
- Split lms/djangoapps/discussion/ into its 4 subdirectories so the heavy
  rest_api/ shard (15.7m) can be distributed across bins independently
- Remove outdated comment referencing unit-tests-gh-hosted.yml

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
contentstore/ is large enough that the cms-1 runner was being killed
mid-run in CI (OOM or runner-level timeout). Splitting it into its own
shard keeps each job under the ~20-25 min target.

No changes needed to gha_unit_tests_collector.py — it already classifies
any shard whose first path starts with "cms/" as a CMS shard.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The --report-log flag adds overhead (writing a JSONL file for every
test) that's only useful for rebalancing work. Skip it entirely on
PR runs by conditionally setting the flag via an env var; also gate
the upload step on master so artifacts aren't created unnecessarily.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Akanshu-2u and others added 30 commits June 10, 2026 11:01
Remove user retirement Day 0 social-auth unlinking from
create retirement so cancel retirement can fully undo.
Fixes for PII annotations

Commit generated by workflow `openedx/openedx-platform/.github/workflows/upgrade-one-python-dependency.yml@refs/heads/master`
…-wiki-66758e9

feat: Upgrade Python dependency openedx-django-wiki
… retirement (#38671)

When ENABLE_REDACT_HISTORICAL_PII_RETIREMENT is enabled,
GeneratedCertificate's django-simple-history table
(certificates_historicalgeneratedcertificate) will also have the user's
name redacted as part of user retirement.
fix: remove annotated models from safelist and annotate openedx models on PII
The PUT /api/contentstore/v1/videos/{course_id}/download endpoint fetched
every client-supplied files[].url server-side with
requests.get(url, allow_redirects=True) and returned the bytes inside the
ZIP response. Because the URLs were never validated, an authenticated user
with studio read access could point them at internal services or cloud
metadata endpoints and exfiltrate the responses (GHSA-fpf9-9rpr-jvrx).

By design these URLs are always a subset of the course's own VAL
encoded_videos[].url values (the same data the video listing hands the
frontend). Restrict fetches to that allowlist: build the set of legitimate
URLs for the course and reject any request containing a URL outside it
before any HTTP request is made. This eliminates the SSRF rather than
merely narrowing it.

Adds VideoDownloadViewTest (the endpoint previously had no test coverage)
covering the allowed-URL success path, rejection of disallowed URLs without
any outbound request, mixed allowed/disallowed batches, and the non-staff
permission gate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fix: int channels safe dict lookup

Commit generated by workflow `openedx/openedx-platform/.github/workflows/upgrade-one-python-dependency.yml@refs/heads/master`
Commit generated by workflow `openedx/openedx-platform/.github/workflows/upgrade-one-python-dependency.yml@refs/heads/master`

Co-authored-by: bcitro <67378070+bcitro@users.noreply.github.com>
django-countries 9.0.0 changed nullable CountryField semantics: a NULL
database value now returns None instead of Country(code=None). Update
all UserProfile.country call sites that previously relied on the old
behavior:

- Replace `country.code is None` checks with `country is None`
  (embargo/api.py, credit/api/provider.py).
- Guard `.code`/`.name` attribute accesses against None
  (credit/tests/factories.py, user_api/accounts/serializers.py,
  courseware/views/views.py).
- Emit "" instead of str(None) == "None" for Segment traits when
  country is null (student/models/user.py, user_authn/views/register.py)
  to preserve existing serialized output and matching test assertions.

Release notes: https://github.com/SmileyChris/django-countries/blob/main/CHANGES.rst#900-10-june-2026

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The 'render the volume control' spec asserted only that *some* element
with class `.volume` existed in the global DOM. It never referenced the
`volumeControl` instance set up in `beforeEach`, so it would pass as
long as any `.volume`-classed element was rendered anywhere — and it ran
synchronously, so it didn't tolerate the rendering completing on a later
tick.

Wait for VideoVolumeControl's own element to attach to its
`.secondary-controls` parent using the `jasmine.waitUntil` pattern
already established in neighboring specs (video_poster_spec.js,
video_progress_slider_spec.js), then assert the element is in the DOM.
Add an explicit `.fail()` branch so the next timeout produces an
actionable error instead of a generic Jasmine timeout.
Before this change, the Student Profile Information CSV that
instructors download from the instructor dashboard showed the literal
text "None" in the city column for users who had not set a city. The
country column for the same users showed up as an empty cell instead.

The cause was a quirk in how the report extracted values: any field
whose Python value was None ended up stringified to "None", but
country was a special object that stringified to "" instead. A 2016
code comment flagged this as "somewhat inconsistent" but it was never
fixed.

The django-countries 9.0.0 upgrade removes the country special case:
an unset country is now plain None, just like city. That made the
country column also show "None", which broke the test that documented
the old quirk.

Rather than accept "None" cells in two columns, this commit fixes the
underlying behaviour so any unset field shows as an empty cell.

BREAKING CHANGE: In the Student Profile Information CSV report, cells
for unset fields (city, country, language, mailing address, etc.)
that used to read the literal text "None" are now empty cells.
Country cells that used to be empty stay empty.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
openedx-authz 1.18.0 added validation that rejects the bare global
scope wildcard '*'. content_libraries calls authz_api.is_user_allowed
with that wildcard from user_can_create_library, so the upgrade broke
LibraryRestoreViewTestCase::test_restore_library_unauthorized (and
likely other code paths exercised at runtime).

Roll openedx-authz back to 1.16.0 until the team owning the
authz migration lands the call-site updates.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
datetime.utcnow() is deprecated in Python 3.12 and scheduled for
removal. Replaces both call sites with datetime.now(UTC) and
updates the session-inactivity middleware tests to mock
datetime.now instead of datetime.utcnow accordingly.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
XBlock.location is deprecated; the documented replacement is
.scope_ids.usage_id. Updates the upstream tag copier to use the
new accessor on both upstream and downstream blocks.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The block_serializer's __init__ accessed self.orig_block_key.course_key,
which emits a DeprecationWarning. .context_key is the documented
replacement and returns the same value for course-block keys, while
also working for library-block keys.

Renames the local variable from course_key to context_key to match.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
models.CheckConstraint.check is deprecated in favor of .condition
(removal scheduled for Django 6.0). Updates the agreement model's
constraint to use the new keyword.

The migration file already uses condition=, so only the model
needed updating.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Django 6.0 will require save()'s force_insert, force_update, using,
and update_fields to be passed as keyword arguments only (positional
support deprecated in 5.1). Updates CourseMode.save() to pass them
by keyword when delegating to super().

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
These openedx-learning models use TypedBigAutoField, which warns
when .pk is accessed outside django.*.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The string '2d' couldn't be parsed by Timedelta.from_json and was
silently coerced to None, triggering ModifyingEnforceTypeWarning
(the assertion happened to pass because enforce_type is disabled
for this codepath and the raw value was preserved). Using a real
timedelta avoids the warning and makes the field type explicit.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4.0.1 does not exist on PyPI, breaking all CI dependency installs.
Pinning back to the last known-good version 4.0.0.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4.0.1 does not exist on PyPI, breaking all CI dependency installs.
Pinning to 4.0.3 which is the latest available version.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Return empty completions_dict for AnonymousUser in CourseNavigationBlocksView to prevent 500 on /api/course_home/v1/navigation/{course_key} for public anonymous access. Keeps existing outline access filtering unchanged.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.