From fee0ef7d85b7744722c5b93c4ffd130563287cca Mon Sep 17 00:00:00 2001 From: Irtaza Akram <51848298+irtazaakram@users.noreply.github.com> Date: Wed, 4 Sep 2024 14:31:37 +0500 Subject: [PATCH] Update Dockerfile & CI workflows to use Ubuntu 24.04 and Python 3.12 (#397) * fix: Updates the Dockerfile to use Ubuntu 24.04 and Python 3.12 * fix: Replaces the deprecated docker-compose command with docker compose * fix: Upgrades Ubuntu to 24.04 in the CI, Docker-publish, and PyPI-publish workflows * fix: replaces pkg_resources with importlib.resources in cookiecutter --- .github/workflows/ci.yml | 3 +- .github/workflows/docker-publish.yml | 2 +- .github/workflows/pypi-publish.yml | 2 +- .isort.cfg | 3 ++ CHANGELOG.rst | 6 +++ Dockerfile | 38 +++++++++---------- Makefile | 10 ++--- README.rst | 23 ++++------- docker-compose.yml | 1 - .../{{cookiecutter.short_name}}.py | 6 +-- pylintrc | 7 ++-- requirements/constraints.txt | 1 - sample_xblocks/basic/content.py | 2 +- sample_xblocks/basic/problem.py | 6 +-- sample_xblocks/filethumbs/filethumbs.py | 2 +- sample_xblocks/filethumbs/static/README.md | 4 +- sample_xblocks/thumbs/static/README.md | 4 +- sample_xblocks/thumbs/thumbs.py | 2 +- setup.py | 3 +- tox.ini | 2 +- workbench/__init__.py | 3 +- workbench/admin.py | 2 - workbench/scenarios.py | 2 +- workbench/test/test_views.py | 4 +- workbench/test_utils.py | 2 +- 25 files changed, 69 insertions(+), 71 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 035be4c3..29e624a0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,10 +8,9 @@ on: - '**' jobs: run_tests: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 strategy: matrix: - os: [ubuntu-20.04] python-version: ['3.11', '3.12'] toxenv: [django42] steps: diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index a826746f..ffa7cf79 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -6,7 +6,7 @@ on: - master jobs: push: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 if: github.event_name == 'push' steps: diff --git a/.github/workflows/pypi-publish.yml b/.github/workflows/pypi-publish.yml index 371a7380..705f3b49 100644 --- a/.github/workflows/pypi-publish.yml +++ b/.github/workflows/pypi-publish.yml @@ -8,7 +8,7 @@ on: jobs: push: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 steps: - name: Checkout diff --git a/.isort.cfg b/.isort.cfg index c0e6985a..d7814842 100644 --- a/.isort.cfg +++ b/.isort.cfg @@ -7,3 +7,6 @@ known_first_party = workbench sample_xblocks sections = FUTURE,STDLIB,THIRDPARTY,DJANGO,DJANGOAPP,EDX,FIRSTPARTY,LOCALFOLDER +known_djangoapp = + workbench + sample_xblocks diff --git a/CHANGELOG.rst b/CHANGELOG.rst index a6cf4fcc..de07d7e8 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,12 @@ Change history for XBlock SDK These are notable changes in XBlock. +0.13.0 - 2024-08-28 +------------------- + +* upgraded to Ubuntu 24.04 and Python 3.12 +* replaced deprecated docker-compose command with docker compose + 0.12.0 - 2024-05-30 ------------------ * dropped python 3.8 support diff --git a/Dockerfile b/Dockerfile index 9d5dee12..b315597a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,33 +1,33 @@ -FROM edxops/focal-common:latest +FROM ubuntu:noble + RUN apt-get update && apt-get install -y \ gettext \ - lib32z1-dev \ + zlib1g-dev \ libjpeg62-dev \ libxslt1-dev \ - zlib1g-dev \ - python3 \ - python3-dev \ - python3-venv \ - python3-pip && \ - pip3 install --upgrade pip setuptools && \ - rm -rf /var/lib/apt/lists/* + python3.12 \ + python3.12-dev \ + python3.12-venv \ + curl \ + make \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* COPY . /usr/local/src/xblock-sdk WORKDIR /usr/local/src/xblock-sdk ENV VIRTUAL_ENV=/venvs/xblock-sdk -RUN python3.11 -m venv $VIRTUAL_ENV +RUN python3.12 -m venv $VIRTUAL_ENV ENV PATH="$VIRTUAL_ENV/bin:$PATH" -RUN pip install --upgrade pip && pip install -r /usr/local/src/xblock-sdk/requirements/dev.txt - -RUN curl -sL https://deb.nodesource.com/setup_14.x -o /tmp/nodejs-setup && \ - /bin/bash /tmp/nodejs-setup && \ +RUN curl -sL https://deb.nodesource.com/setup_22.x -o /tmp/nodejs-setup && \ + bash /tmp/nodejs-setup && \ rm /tmp/nodejs-setup && \ - apt-get -y install nodejs && \ - echo $PYTHONPATH && \ - make install + apt-get install -y nodejs + +RUN pip install --upgrade pip setuptools +RUN make install EXPOSE 8000 -ENTRYPOINT ["python", "manage.py"] -CMD ["runserver", "0.0.0.0:8000"] + +ENTRYPOINT ["bash", "-c", "python manage.py migrate && exec python manage.py runserver 0.0.0.0:8000"] diff --git a/Makefile b/Makefile index ca7390de..ff102104 100755 --- a/Makefile +++ b/Makefile @@ -97,20 +97,20 @@ selfcheck: ## check that the Makefile is well-formed @echo "The Makefile is well-formed." docker_build: - docker-compose build + docker compose build # devstack-themed shortcuts dev.up: # Starts all containers - docker-compose up -d + docker compose up -d dev.up.build: - docker-compose up -d --build + docker compose up -d --build dev.down: # Kills containers and all of their data that isn't in volumes - docker-compose down + docker compose down dev.stop: # Stops containers so they can be restarted - docker-compose stop + docker compose stop app-shell: # Run bash in the container as root docker exec -u 0 -it edx.devstack.xblock-sdk bash diff --git a/README.rst b/README.rst index 03a1da2b..0d6afb31 100644 --- a/README.rst +++ b/README.rst @@ -65,7 +65,7 @@ Locally Docker ~~~~~~ -Alternatively, you can build and run the xblock-sdk in Docker (we are using docker-compose which +Alternatively, you can build and run the xblock-sdk in Docker (we are using docker compose which can be installed as explained at https://docs.docker.com/compose/install/) After cloning this repository locally, go into the repository directory and build the Docker image:: @@ -74,7 +74,7 @@ After cloning this repository locally, go into the repository directory and buil or manually run - $ docker-compose build + $ docker compose build You can then run the locally-built version using the following command:: @@ -82,15 +82,17 @@ You can then run the locally-built version using the following command:: or manually run:: - $ docker-compose up -d + $ docker compose up -d -and stop the container (without removing data) by:: +You should now be able to access the XBlock SDK environment in your browser at http://localhost:8000 + +To stop the container (without removing data) by:: $ make dev.stop or manually run:: - $ docker-compose stop + $ docker compose stop Note, using:: @@ -98,19 +100,10 @@ Note, using:: or:: - $ docker-compose down + $ docker compose down will shut down the container and delete non-persistent data. -On the first startup run the following command to create the SQLite database. -(Otherwise you will get an error no such table: workbench_xblockstate.) - -Command:: - - $ docker container exec -it edx.devstack.xblock-sdk python3.11 manage.py migrate - -You should now be able to access the XBlock SDK environment in your browser at http://localhost:8000 - You can open a bash shell in the running container by using:: $ make app-shell diff --git a/docker-compose.yml b/docker-compose.yml index b7646e56..063328ee 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,3 @@ -version: '3.5' services: xblock_sdk: image: openedx/xblock-sdk diff --git a/prototype/{{cookiecutter.short_name}}/{{cookiecutter.short_name}}/{{cookiecutter.short_name}}.py b/prototype/{{cookiecutter.short_name}}/{{cookiecutter.short_name}}/{{cookiecutter.short_name}}.py index 5a8c861e..bd407ccd 100644 --- a/prototype/{{cookiecutter.short_name}}/{{cookiecutter.short_name}}/{{cookiecutter.short_name}}.py +++ b/prototype/{{cookiecutter.short_name}}/{{cookiecutter.short_name}}/{{cookiecutter.short_name}}.py @@ -1,6 +1,7 @@ """TO-DO: Write a description of what this XBlock is.""" -import pkg_resources +from importlib.resources import files + from web_fragments.fragment import Fragment from xblock.core import XBlock from xblock.fields import Integer, Scope @@ -22,8 +23,7 @@ class {{cookiecutter.class_name}}(XBlock): def resource_string(self, path): """Handy helper for getting resources from our kit.""" - data = pkg_resources.resource_string(__name__, path) - return data.decode("utf8") + return files(__package__).joinpath(path).read_text(encoding="utf-8") # TO-DO: change this view to display your data your own way. def student_view(self, context=None): diff --git a/pylintrc b/pylintrc index 3d3719f3..39e25136 100644 --- a/pylintrc +++ b/pylintrc @@ -64,7 +64,7 @@ # SERIOUSLY. # # ------------------------------ -# Generated by edx-lint version: 5.2.5 +# Generated by edx-lint version: 5.4.0 # ------------------------------ [MASTER] ignore = migrations @@ -259,6 +259,7 @@ enable = useless-suppression, disable = bad-indentation, + broad-exception-raised, consider-using-f-string, duplicate-code, file-ignored, @@ -380,6 +381,6 @@ ext-import-graph = int-import-graph = [EXCEPTIONS] -overgeneral-exceptions = Exception +overgeneral-exceptions = builtins.Exception -# 15f4c0e4571a74bff869eddc0b2e573520a58d0c +# 80dd86efa4e64209cd8e26c39b1b42ccdcb57351 diff --git a/requirements/constraints.txt b/requirements/constraints.txt index add1653d..a51cb08b 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -10,4 +10,3 @@ # Common constraints for edx repos -c common_constraints.txt -backports.zoneinfo;python_version<"3.9" diff --git a/sample_xblocks/basic/content.py b/sample_xblocks/basic/content.py index efe0a7aa..ef837c71 100644 --- a/sample_xblocks/basic/content.py +++ b/sample_xblocks/basic/content.py @@ -112,7 +112,7 @@ def fallback_view(self, view_name, context=None): # pylint: disable=W0613 value=getattr(self, field_name), help=field.help ) - for field_name, field in self.fields.items() # pylint: disable=no-member + for field_name, field in self.fields.items() if field_name not in ["name", "parent", "tags"] ] diff --git a/sample_xblocks/basic/problem.py b/sample_xblocks/basic/problem.py index 104a7f7d..2ce495ad 100644 --- a/sample_xblocks/basic/problem.py +++ b/sample_xblocks/basic/problem.py @@ -98,7 +98,7 @@ def student_view(self, context=None): named_child_frags = [] # self.children is an attribute obtained from ChildrenModelMetaclass, so disable the # static pylint checking warning about this. - for child_id in self.children: # pylint: disable=E1101 + for child_id in self.children: child = self.runtime.get_block(child_id) frag = self.runtime.render_child(child, "problem_view", context) result.add_fragment_resources(frag) @@ -175,7 +175,7 @@ def check(self, submissions, suffix=''): # pylint: disable=unused-argument child_map = {} # self.children is an attribute obtained from ChildrenModelMetaclass, so disable the # static pylint checking warning about this. - for child_id in self.children: # pylint: disable=E1101 + for child_id in self.children: child = self.runtime.get_block(child_id) if child.name: child_map[child.name] = child @@ -334,7 +334,7 @@ def set_arguments_from_xml(self, node): # pylint: disable=no-member, useless-suppression, deprecated-method argspec = inspect.getfullargspec(self.check) else: - argspec = inspect.getargspec(self.check) # pylint: disable=deprecated-method + argspec = inspect.getargspec(self.check) # pylint: disable=no-member arguments = {} for arg in argspec.args[1:]: arguments[arg] = node.attrib.pop(arg) diff --git a/sample_xblocks/filethumbs/filethumbs.py b/sample_xblocks/filethumbs/filethumbs.py index 22ce39aa..859761b6 100644 --- a/sample_xblocks/filethumbs/filethumbs.py +++ b/sample_xblocks/filethumbs/filethumbs.py @@ -17,10 +17,10 @@ """ +import importlib.resources import json import logging -import importlib.resources import png from web_fragments.fragment import Fragment from xblock.core import XBlock diff --git a/sample_xblocks/filethumbs/static/README.md b/sample_xblocks/filethumbs/static/README.md index eaf62eb9..3b539400 100644 --- a/sample_xblocks/filethumbs/static/README.md +++ b/sample_xblocks/filethumbs/static/README.md @@ -1,12 +1,12 @@ # XBlock Static Files An XBlock can load resources from its package using Python's -[pkg_resources](http://pythonhosted.org/distribute/pkg_resources.html). +[importlib.resources](https://docs.python.org/3/library/importlib.resources.html). We use a directory structure with folders for `css`, `html`, and `js` files. However, this structure is not mandatory. Each XBlock can choose its directory structure, as long as it specifies the correct -paths to `pkg_resources`. +paths to `importlib.resources`. We include unit tests for JavaScript in the `js` folder. See `js/README.md` for details on how to run these tests. diff --git a/sample_xblocks/thumbs/static/README.md b/sample_xblocks/thumbs/static/README.md index eaf62eb9..3b539400 100644 --- a/sample_xblocks/thumbs/static/README.md +++ b/sample_xblocks/thumbs/static/README.md @@ -1,12 +1,12 @@ # XBlock Static Files An XBlock can load resources from its package using Python's -[pkg_resources](http://pythonhosted.org/distribute/pkg_resources.html). +[importlib.resources](https://docs.python.org/3/library/importlib.resources.html). We use a directory structure with folders for `css`, `html`, and `js` files. However, this structure is not mandatory. Each XBlock can choose its directory structure, as long as it specifies the correct -paths to `pkg_resources`. +paths to `importlib.resources`. We include unit tests for JavaScript in the `js` folder. See `js/README.md` for details on how to run these tests. diff --git a/sample_xblocks/thumbs/thumbs.py b/sample_xblocks/thumbs/thumbs.py index b1ba1bec..e47184e5 100644 --- a/sample_xblocks/thumbs/thumbs.py +++ b/sample_xblocks/thumbs/thumbs.py @@ -1,8 +1,8 @@ """An XBlock providing thumbs-up/thumbs-down voting.""" +import importlib.resources import logging -import importlib.resources from web_fragments.fragment import Fragment from xblock.core import XBlock, XBlockAside from xblock.fields import Boolean, Integer, Scope diff --git a/setup.py b/setup.py index 748ce869..1d4d0239 100644 --- a/setup.py +++ b/setup.py @@ -121,8 +121,7 @@ def get_version(*file_paths): classifiers=[ 'Development Status :: 3 - Alpha', 'Framework :: Django', - 'Framework :: Django :: 3.2', - 'Framework :: Django :: 4.0', + 'Framework :: Django :: 4.2', 'Intended Audience :: Developers', 'License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)', 'Natural Language :: English', diff --git a/tox.ini b/tox.ini index a0b254bb..96cfce73 100644 --- a/tox.ini +++ b/tox.ini @@ -27,4 +27,4 @@ deps = -r{toxinidir}/requirements/quality.txt commands = pylint workbench sample_xblocks - isort --check-only . + isort --check-only workbench sample_xblocks diff --git a/workbench/__init__.py b/workbench/__init__.py index 7f182161..ff263449 100644 --- a/workbench/__init__.py +++ b/workbench/__init__.py @@ -1,5 +1,6 @@ +# pylint: disable=django-not-configured """ Provide a djangoapp for XBlock development """ -__version__ = '0.12.0' +__version__ = '0.13.0' diff --git a/workbench/admin.py b/workbench/admin.py index 89c2c8ab..68295f0d 100644 --- a/workbench/admin.py +++ b/workbench/admin.py @@ -24,5 +24,3 @@ class XBlockStateAdmin(admin.ModelAdmin): readonly_fields = [ 'scope', 'scope_id', 'scenario', 'tag', 'user_id', 'created' ] - - diff --git a/workbench/scenarios.py b/workbench/scenarios.py index a47e195e..add24a78 100644 --- a/workbench/scenarios.py +++ b/workbench/scenarios.py @@ -56,7 +56,7 @@ def add_class_scenarios(class_name, cls, fail_silently=True): scname = "%s.%d" % (class_name, i) try: add_xml_scenario(scname, desc, xml) - except Exception: # pylint:disable=broad-except + except Exception: # pylint: disable=broad-exception-caught # don't allow a single bad scenario to block the whole workbench if fail_silently: log.warning("Cannot load %s", desc, exc_info=True) diff --git a/workbench/test/test_views.py b/workbench/test/test_views.py index a73bf218..df5b8a78 100644 --- a/workbench/test/test_views.py +++ b/workbench/test/test_views.py @@ -178,7 +178,7 @@ def student_view(self, context=None): # pylint: disable=W0613 ] urls = [] for args in all_args: - thirdparty = (args[0] == "send_it_back_public") + thirdparty = args[0] == "send_it_back_public" urls.append(self.runtime.handler_url(self, *args, thirdparty=thirdparty)) encoded = json.dumps(urls) return Fragment(":::" + encoded + ":::") @@ -333,7 +333,7 @@ def student_view(self, context): @temp_scenario(XBlockWithContextTracking, 'context_tracking') def test_activate_id(): client = Client() - assert XBlockWithContextTracking.registered_contexts == [] # precondition check + assert not XBlockWithContextTracking.registered_contexts # precondition check client.get("/view/context_tracking/") assert XBlockWithContextTracking.registered_contexts == [{'activate_block_id': None}] diff --git a/workbench/test_utils.py b/workbench/test_utils.py index 6456b2cd..36d01068 100644 --- a/workbench/test_utils.py +++ b/workbench/test_utils.py @@ -123,7 +123,7 @@ def request(self, xblock, handler_name, content, request_method="POST", response Content of the response (mixed). """ # Create a fake request - request = webob.Request(dict()) + request = webob.Request({}) request.method = request_method request.body = content