Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 78 additions & 28 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,46 +6,96 @@ on:
branches:
- nau/*.master

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:

test:
test-and-lint:
runs-on: ubuntu-latest
strategy:
matrix:
include:
- edx_platform_repository: fccn/edx-platform
edx_platform_ref: nau/redwood.master
python-version: "3.11"
steps:
- uses: actions/checkout@v4
- name: Checkout nau-openedx-extensions
uses: actions/checkout@v4
with:
path: nau-openedx-extensions

- name: Cache edx-platform repository
uses: actions/cache@v4
with:
path: edx-platform
key: edx-platform-${{ matrix.edx_platform_repository }}-${{ matrix.edx_platform_ref }}-${{ github.sha }}
restore-keys: |
edx-platform-${{ matrix.edx_platform_repository }}-${{ matrix.edx_platform_ref }}-
edx-platform-${{ matrix.edx_platform_repository }}-

- name: Clone edx-platform
run: |
git clone --depth 1 https://github.com/openedx/edx-platform.git ${{ github.workspace }}/../edx-platform
- name: Checkout edx-platform
uses: actions/checkout@v4
with:
repository: ${{ matrix.edx_platform_repository }}
ref: ${{ matrix.edx_platform_ref }}
path: edx-platform

- uses: actions/setup-python@v5
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
cache-dependency-path: requirements/*.txt
python-version: '${{ matrix.python-version }}'

- run: make requirements
- run: make test
- name: Install system requirements
run: sudo apt update && sudo apt install -y libxmlsec1-dev

lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- name: Verify Docker is available
run: docker --version

- name: Cache virtual environment
uses: actions/cache@v4
with:
python-version: '3.11'
cache: 'pip'
cache-dependency-path: requirements/*.txt
- run: make requirements
- run: make lint
path: nau-openedx-extensions/venv
key: venv-v3-${{ runner.os }}-py${{ matrix.python-version }}-${{ hashFiles('edx-platform/requirements/edx/testing.txt', 'nau-openedx-extensions/requirements/*.txt') }}
restore-keys: |
venv-v3-${{ runner.os }}-py${{ matrix.python-version }}-

- name: Install requirements
run: EDX_PLATFORM_PATH=${{ github.workspace }}/edx-platform make -C nau-openedx-extensions requirements

- name: Run lint
run: EDX_PLATFORM_PATH=${{ github.workspace }}/edx-platform make -C nau-openedx-extensions lint

- name: Run tests
run: EDX_PLATFORM_PATH=${{ github.workspace }}/edx-platform make -C nau-openedx-extensions test
timeout-minutes: 60

check_miss_run_update_translations:
runs-on: ubuntu-latest
strategy:
matrix:
include:
- python-version: "3.11"
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- name: Checkout nau-openedx-extensions
uses: actions/checkout@v4

- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
cache-dependency-path: requirements/*.txt
- run: make requirements
- run: make check_miss_run_update_translations
python-version: '${{ matrix.python-version }}'

- name: Cache pip packages
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: pip-translations-${{ runner.os }}-py${{ matrix.python-version }}-${{ hashFiles('requirements/translations.txt') }}
restore-keys: |
pip-translations-${{ runner.os }}-py${{ matrix.python-version }}

- name: Install translation requirements
run: python -m pip install -r requirements/translations.txt

- name: Check translations
run: make check_miss_run_update_translations
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.11
207 changes: 182 additions & 25 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,46 +7,203 @@
.DEFAULT_GOAL := help
.PHONY: help

ifdef TOXENV
TOX := tox -- #to isolate each tox environment if TOXENV is defined
endif
# ==============================================================================
# VARIABLES
# ==============================================================================
# current directory relative to the Makefile file
ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))

# By default use the sibling edx-platform folder
# but you can use other folder, you just have to change this environment variable like:
# EDX_PLATFORM_PATH=/nau make test
# make EDX_PLATFORM_PATH=/nau test
EDX_PLATFORM_PATH ?= $(shell dirname $(ROOT_DIR))/edx-platform

# Virtual environment
VENV_DIR := $(ROOT_DIR)/venv
VENV_BIN := $(VENV_DIR)/bin
PYTHON := $(VENV_BIN)/python
PIP := $(VENV_BIN)/pip
COVERAGE := $(VENV_BIN)/coverage
PYTEST := $(VENV_BIN)/pytest

# MongoDB variables for testing
MONGO_CONTAINER_NAME := nau-test-mongodb
MONGO_PORT := 27017
MONGO_IMAGE := mongo:7


help: ## display this help message
@echo "Please use \`make <target>' where <target> is one of"
@grep '^[a-zA-Z]' $(MAKEFILE_LIST) | sort | awk -F ':.*?## ' 'NF==2 {printf "\033[36m %-25s\033[0m %s\n", $$1, $$2}'

_prerequire: ## Check that edx-platform directory exists
@if [ ! -d "${EDX_PLATFORM_PATH}" ]; then { echo "edx-platform directory doesn't exist.\n EDX_PLATFORM_PATH=${EDX_PLATFORM_PATH}\nPlease check if that directory exists or change the default value using:\n EDX_PLATFORM_PATH=~/<different path>/edx-platform make <target>" ; exit 1; } fi
.PHONY: _prerequire

clean: ## delete most git-ignored files
find . -name '__pycache__' -exec rm -rf {} +
find . -name '*.pyc' -exec rm -f {} +
find . -name '*.pyo' -exec rm -f {} +
find . -name '*~' -exec rm -f {} +
echo "cleaned"
@find . -name '__pycache__' -exec rm -rf {} +
@find . -name '*.pyc' -exec rm -f {} +
@find . -name '*.pyo' -exec rm -f {} +
@find . -name '*~' -exec rm -f {} +
@-rm -rf .coverage .coverage.*
@echo "✓ Cleaned"
# rm -rf venv +
.PHONY: clean

requirements: ## Install requirements
python -m pip install -r requirements/base.txt
python -m pip install -r requirements/translations.txt
python -m pip install -r requirements/test.txt
_check_python: ## Check Python version is 3.11
@python_version=$$(python3 --version 2>&1 | grep -oP '3\.11\.\d+'); \
if [ -z "$$python_version" ]; then \
echo "Error: Python 3.11 is required but not found."; \
echo "Current version: $$(python3 --version)"; \
exit 1; \
else \
echo "✓ Using Python $$python_version"; \
fi
.PHONY: _check_python

_venv: _check_python ## Create virtual environment if it doesn't exist
@if [ ! -d "$(VENV_DIR)" ]; then \
echo "Creating virtual environment with Python 3.11..."; \
python3 -m venv $(VENV_DIR); \
echo "✓ Virtual environment created at $(VENV_DIR)"; \
fi
@venv_python_version=$$($(PYTHON) --version 2>&1 | grep -oP '3\.11\.\d+'); \
if [ -z "$$venv_python_version" ]; then \
echo "Error: Virtual environment is not using Python 3.11"; \
echo "Virtual environment Python: $$($(PYTHON) --version)"; \
echo "Please remove the venv directory and try again: rm -rf $(VENV_DIR)"; \
exit 1; \
fi
.PHONY: _venv

requirements: | _prerequire _venv ## Install requirements from both edx-platform and nau-openedx-extensions
@echo "Installing edx-platform testing requirements..."
$(PIP) install -r ${EDX_PLATFORM_PATH}/requirements/edx/testing.txt
@echo "Installing edx-platform in editable mode..."
$(PIP) install -e ${EDX_PLATFORM_PATH}
@echo "Installing nau-openedx-extensions test requirements..."
$(PIP) install -r $(ROOT_DIR)/requirements/test.in
@echo "Installing nau-openedx-extensions in editable mode..."
$(PIP) install -e $(ROOT_DIR)
@echo "✓ All requirements installed successfully"
.PHONY: requirements

test: clean requirements
$(TOX) PYTHONPATH=$(PWD):../edx-platform \
DJANGO_SETTINGS_MODULE=lms.envs.tutor.production \
coverage run --source="nau_openedx_extensions" -m pytest --rootdir=/.. nau_openedx_extensions
$(TOX) coverage report --fail-under=5
# ==============================================================================
# MONGODB TARGETS
# ==============================================================================

mongo-start: ## Start MongoDB container for testing (localhost only)
@if docker ps -a --format '{{.Names}}' | grep -q "^$(MONGO_CONTAINER_NAME)$$"; then \
if docker ps --format '{{.Names}}' | grep -q "^$(MONGO_CONTAINER_NAME)$$"; then \
echo "MongoDB container '$(MONGO_CONTAINER_NAME)' is already running"; \
else \
echo "Starting existing MongoDB container '$(MONGO_CONTAINER_NAME)'..."; \
docker start $(MONGO_CONTAINER_NAME); \
$(MAKE) --no-print-directory mongo-ping; \
fi \
else \
echo "Creating and starting MongoDB container '$(MONGO_CONTAINER_NAME)'..."; \
docker run -d --name $(MONGO_CONTAINER_NAME) -p 127.0.0.1:$(MONGO_PORT):27017 $(MONGO_IMAGE); \
$(MAKE) --no-print-directory mongo-ping; \
fi
.PHONY: mongo-start

mongo-stop: ## Stop and remove MongoDB container
@if docker ps --format '{{.Names}}' | grep -q "^$(MONGO_CONTAINER_NAME)$$"; then \
echo "Stopping MongoDB container '$(MONGO_CONTAINER_NAME)'..."; \
docker stop $(MONGO_CONTAINER_NAME); \
docker rm $(MONGO_CONTAINER_NAME); \
echo "MongoDB container stopped and removed"; \
else \
if docker ps -a --format '{{.Names}}' | grep -q "^$(MONGO_CONTAINER_NAME)$$"; then \
echo "Removing stopped MongoDB container '$(MONGO_CONTAINER_NAME)'..."; \
docker rm $(MONGO_CONTAINER_NAME); \
else \
echo "MongoDB container '$(MONGO_CONTAINER_NAME)' is not running"; \
fi \
fi
.PHONY: mongo-stop

mongo-status: ## Check MongoDB container status
@if docker ps --format '{{.Names}}' | grep -q "^$(MONGO_CONTAINER_NAME)$$"; then \
echo "✓ MongoDB container '$(MONGO_CONTAINER_NAME)' is running"; \
docker ps --filter "name=$(MONGO_CONTAINER_NAME)" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"; \
else \
if docker ps -a --format '{{.Names}}' | grep -q "^$(MONGO_CONTAINER_NAME)$$"; then \
echo "✗ MongoDB container '$(MONGO_CONTAINER_NAME)' exists but is not running"; \
else \
echo "✗ MongoDB container '$(MONGO_CONTAINER_NAME)' does not exist"; \
fi; \
exit 1; \
fi
.PHONY: mongo-status

mongo-ping: ## Wait for MongoDB to be ready
@echo "Waiting for MongoDB to be ready..."
@for i in 1 2 3 4 5 6 7 8 9 10; do \
if docker exec $(MONGO_CONTAINER_NAME) mongosh --quiet --eval "db.runCommand({ ping: 1 })" >/dev/null 2>&1; then \
echo "✓ MongoDB is ready"; \
exit 0; \
fi; \
echo " Attempt $$i/10: MongoDB not ready yet, waiting..."; \
sleep 1; \
done; \
echo "✗ MongoDB failed to start within 10 seconds"; \
exit 1
.PHONY: mongo-ping

_mongo_ensure_running: ## Internal: ensure MongoDB is running
@if ! docker ps --format '{{.Names}}' | grep -q "^$(MONGO_CONTAINER_NAME)$$"; then \
echo "MongoDB is not running. Starting MongoDB container..."; \
$(MAKE) --no-print-directory mongo-start; \
fi
.PHONY: _mongo_ensure_running

_mongo_cleanup: ## Internal: cleanup MongoDB after tests
@trap '$(MAKE) --no-print-directory mongo-stop' EXIT; \
$(MAKE) --no-print-directory _run_tests ARGS="$(ARGS)"
.PHONY: _mongo_cleanup

_run_tests: clean | _prerequire _venv ## Internal: run the actual tests
@args="$(filter-out $@,$(MAKECMDGOALS))" && \
arg_2="$${args:-${ARGS}}" && \
arg_3="$${arg_2:=$(ROOT_DIR)/nau_openedx_extensions}" && \
cd ${EDX_PLATFORM_PATH} && \
PYTHONPATH=${EDX_PLATFORM_PATH}:$(ROOT_DIR) \
EDXAPP_TEST_MONGO_HOST=localhost \
EDXAPP_TEST_MONGO_PORT=$(MONGO_PORT) \
$(COVERAGE) run --source="$(ROOT_DIR)/nau_openedx_extensions" -m pytest --ds=nau_openedx_extensions.settings.test --nomigrations --reuse-db -o django_find_project=false $${arg_3}
@cd ${EDX_PLATFORM_PATH} && $(COVERAGE) combine || true
@cd ${EDX_PLATFORM_PATH} && $(COVERAGE) report --include="$(ROOT_DIR)/nau_openedx_extensions/*" --fail-under=5
@echo "✓ Tests passed"
.PHONY: _run_tests

test: _prerequire ## Run tests with auto-managed MongoDB (starts, runs tests, stops). Usage: make test `pwd`/[path/to/test_file.py::TestClass::test_method]
@$(MAKE) --no-print-directory mongo-start
@cleanup() { $(MAKE) --no-print-directory mongo-stop; }; \
trap cleanup EXIT INT TERM; \
args="$(filter-out $@,$(MAKECMDGOALS))"; \
$(MAKE) --no-print-directory _run_tests ARGS="$$args"
.PHONY: test

lint: ## Run linters to check code style
$(TOX) pylint ./nau_openedx_extensions
$(TOX) pycodestyle ./nau_openedx_extensions
$(TOX) isort --check-only --diff ./nau_openedx_extensions
test-keep-mongo: _prerequire ## Run tests with MongoDB (starts if needed, keeps running after). Usage: make test-keep-mongo `pwd`/[path/to/test_file.py::TestClass::test_method]
@$(MAKE) --no-print-directory _mongo_ensure_running
@args="$(filter-out $@,$(MAKECMDGOALS))" && \
$(MAKE) --no-print-directory _run_tests ARGS="$$args"
.PHONY: test-keep-mongo

lint: | _prerequire _venv ## Run linters to check code style
@cd ${EDX_PLATFORM_PATH} && PYTHONPATH=${EDX_PLATFORM_PATH}:$(ROOT_DIR) $(VENV_BIN)/pylint $(ROOT_DIR)/nau_openedx_extensions
@cd ${EDX_PLATFORM_PATH} && PYTHONPATH=${EDX_PLATFORM_PATH}:$(ROOT_DIR) $(VENV_BIN)/pycodestyle $(ROOT_DIR)/nau_openedx_extensions
@cd ${EDX_PLATFORM_PATH} && PYTHONPATH=${EDX_PLATFORM_PATH}:$(ROOT_DIR) $(VENV_BIN)/isort --check-only --diff $(ROOT_DIR)/nau_openedx_extensions
@echo "✓ Linting passed"
.PHONY: lint

lint-fix: ## Fix Python import sort
$(TOX) isort ./nau_openedx_extensions
$(TOX) autopep8 --in-place --aggressive --aggressive ./nau_openedx_extensions/*.py
lint-fix: | _prerequire _venv ## Fix Python import sort
@cd ${EDX_PLATFORM_PATH} && PYTHONPATH=${EDX_PLATFORM_PATH}:$(ROOT_DIR) $(VENV_BIN)/isort $(ROOT_DIR)/nau_openedx_extensions
@cd ${EDX_PLATFORM_PATH} && PYTHONPATH=${EDX_PLATFORM_PATH}:$(ROOT_DIR) $(VENV_BIN)/autopep8 --in-place --aggressive --aggressive $(ROOT_DIR)/nau_openedx_extensions/*.py
@echo "✓ Linting fixes applied"
.PHONY: lint-fix

# Define PIP_COMPILE_OPTS=-v to get more information during make upgrade.
Expand Down Expand Up @@ -81,7 +238,7 @@ compile-requirements: pre-requirements ## Re-compile *.in requirements to *.txt
.PHONY: compile-requirements

upgrade: ## update the pip requirements files to use the latest releases satisfying our constraints
$(MAKE) compile-requirements COMPILE_OPTS="--upgrade"
$(MAKE) --no-print-directory compile-requirements COMPILE_OPTS="--upgrade"
.PHONY: upgrade

# TODO: define somewhere else
Expand Down
8 changes: 3 additions & 5 deletions nau_openedx_extensions/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,13 @@ class NauOpenEdxConfig(AppConfig):
},
"settings_config": {
"lms.djangoapp": {
"test": {"relative_path": "settings.test"},
"common": {"relative_path": "settings.common"},
"production": {"relative_path": "settings.production"},
},
'cms.djangoapp': {
"cms.djangoapp": {
'common': {'relative_path': 'settings.common'},
'test': {'relative_path': 'settings.test'},
"production": {"relative_path": "settings.production"},
},
'production': {'relative_path': 'settings.production'},
}
},
"view_context_config": {
"lms.djangoapp": {
Expand Down
1 change: 0 additions & 1 deletion nau_openedx_extensions/coursecertificate/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ class CoursecertificateConfig(AppConfig):
"settings_config": {
"lms.djangoapp": {
"common": {"relative_path": "settings.common"},
"test": {"relative_path": "settings.test"},
"production": {"relative_path": "settings.production"},
},
},
Expand Down
Loading