|
7 | 7 | .DEFAULT_GOAL := help |
8 | 8 | .PHONY: help |
9 | 9 |
|
10 | | -ifdef TOXENV |
11 | | -TOX := tox -- #to isolate each tox environment if TOXENV is defined |
12 | | -endif |
| 10 | +# ============================================================================== |
| 11 | +# VARIABLES |
| 12 | +# ============================================================================== |
| 13 | +# current directory relative to the Makefile file |
| 14 | +ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) |
| 15 | + |
| 16 | +# By default use the sibling edx-platform folder |
| 17 | +# but you can use other folder, you just have to change this environment variable like: |
| 18 | +# EDX_PLATFORM_PATH=/nau make test |
| 19 | +# make EDX_PLATFORM_PATH=/nau test |
| 20 | +EDX_PLATFORM_PATH ?= $(shell dirname $(ROOT_DIR))/edx-platform |
| 21 | + |
| 22 | +# Virtual environment |
| 23 | +VENV_DIR := $(ROOT_DIR)/venv |
| 24 | +VENV_BIN := $(VENV_DIR)/bin |
| 25 | +PYTHON := $(VENV_BIN)/python |
| 26 | +PIP := $(VENV_BIN)/pip |
| 27 | +COVERAGE := $(VENV_BIN)/coverage |
| 28 | +PYTEST := $(VENV_BIN)/pytest |
| 29 | + |
| 30 | +# MongoDB variables for testing |
| 31 | +MONGO_CONTAINER_NAME := nau-test-mongodb |
| 32 | +MONGO_PORT := 27017 |
| 33 | +MONGO_IMAGE := mongo:7 |
13 | 34 |
|
14 | 35 |
|
15 | 36 | help: ## display this help message |
16 | 37 | @echo "Please use \`make <target>' where <target> is one of" |
17 | 38 | @grep '^[a-zA-Z]' $(MAKEFILE_LIST) | sort | awk -F ':.*?## ' 'NF==2 {printf "\033[36m %-25s\033[0m %s\n", $$1, $$2}' |
18 | 39 |
|
| 40 | +_prerequire: ## Check that edx-platform directory exists |
| 41 | + @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 |
| 42 | +.PHONY: _prerequire |
| 43 | + |
19 | 44 | clean: ## delete most git-ignored files |
20 | | - find . -name '__pycache__' -exec rm -rf {} + |
21 | | - find . -name '*.pyc' -exec rm -f {} + |
22 | | - find . -name '*.pyo' -exec rm -f {} + |
23 | | - find . -name '*~' -exec rm -f {} + |
24 | | - echo "cleaned" |
| 45 | + @find . -name '__pycache__' -exec rm -rf {} + |
| 46 | + @find . -name '*.pyc' -exec rm -f {} + |
| 47 | + @find . -name '*.pyo' -exec rm -f {} + |
| 48 | + @find . -name '*~' -exec rm -f {} + |
| 49 | + @-rm -rf .coverage .coverage.* |
| 50 | + @echo "✓ Cleaned" |
25 | 51 | # rm -rf venv + |
26 | 52 | .PHONY: clean |
27 | 53 |
|
28 | | -requirements: ## Install requirements |
29 | | - python -m pip install -r requirements/base.txt |
30 | | - python -m pip install -r requirements/translations.txt |
31 | | - python -m pip install -r requirements/test.txt |
| 54 | +_check_python: ## Check Python version is 3.11 |
| 55 | + @python_version=$$(python3 --version 2>&1 | grep -oP '3\.11\.\d+'); \ |
| 56 | + if [ -z "$$python_version" ]; then \ |
| 57 | + echo "Error: Python 3.11 is required but not found."; \ |
| 58 | + echo "Current version: $$(python3 --version)"; \ |
| 59 | + exit 1; \ |
| 60 | + else \ |
| 61 | + echo "✓ Using Python $$python_version"; \ |
| 62 | + fi |
| 63 | +.PHONY: _check_python |
| 64 | + |
| 65 | +_venv: _check_python ## Create virtual environment if it doesn't exist |
| 66 | + @if [ ! -d "$(VENV_DIR)" ]; then \ |
| 67 | + echo "Creating virtual environment with Python 3.11..."; \ |
| 68 | + python3 -m venv $(VENV_DIR); \ |
| 69 | + echo "✓ Virtual environment created at $(VENV_DIR)"; \ |
| 70 | + fi |
| 71 | + @venv_python_version=$$($(PYTHON) --version 2>&1 | grep -oP '3\.11\.\d+'); \ |
| 72 | + if [ -z "$$venv_python_version" ]; then \ |
| 73 | + echo "Error: Virtual environment is not using Python 3.11"; \ |
| 74 | + echo "Virtual environment Python: $$($(PYTHON) --version)"; \ |
| 75 | + echo "Please remove the venv directory and try again: rm -rf $(VENV_DIR)"; \ |
| 76 | + exit 1; \ |
| 77 | + fi |
| 78 | +.PHONY: _venv |
| 79 | + |
| 80 | +requirements: | _prerequire _venv ## Install requirements from both edx-platform and nau-openedx-extensions |
| 81 | + @echo "Installing edx-platform testing requirements..." |
| 82 | + $(PIP) install -r ${EDX_PLATFORM_PATH}/requirements/edx/testing.txt |
| 83 | + @echo "Installing edx-platform in editable mode..." |
| 84 | + $(PIP) install -e ${EDX_PLATFORM_PATH} |
| 85 | + @echo "Installing nau-openedx-extensions test requirements..." |
| 86 | + $(PIP) install -r $(ROOT_DIR)/requirements/test.in |
| 87 | + @echo "Installing nau-openedx-extensions in editable mode..." |
| 88 | + $(PIP) install -e $(ROOT_DIR) |
| 89 | + @echo "✓ All requirements installed successfully" |
32 | 90 | .PHONY: requirements |
33 | 91 |
|
34 | | -test: clean requirements |
35 | | - $(TOX) PYTHONPATH=$(PWD):../edx-platform \ |
36 | | - DJANGO_SETTINGS_MODULE=lms.envs.tutor.production \ |
37 | | - coverage run --source="nau_openedx_extensions" -m pytest --rootdir=/.. nau_openedx_extensions |
38 | | - $(TOX) coverage report --fail-under=5 |
| 92 | +# ============================================================================== |
| 93 | +# MONGODB TARGETS |
| 94 | +# ============================================================================== |
| 95 | + |
| 96 | +mongo-start: ## Start MongoDB container for testing (localhost only) |
| 97 | + @if docker ps -a --format '{{.Names}}' | grep -q "^$(MONGO_CONTAINER_NAME)$$"; then \ |
| 98 | + if docker ps --format '{{.Names}}' | grep -q "^$(MONGO_CONTAINER_NAME)$$"; then \ |
| 99 | + echo "MongoDB container '$(MONGO_CONTAINER_NAME)' is already running"; \ |
| 100 | + else \ |
| 101 | + echo "Starting existing MongoDB container '$(MONGO_CONTAINER_NAME)'..."; \ |
| 102 | + docker start $(MONGO_CONTAINER_NAME); \ |
| 103 | + $(MAKE) --no-print-directory mongo-ping; \ |
| 104 | + fi \ |
| 105 | + else \ |
| 106 | + echo "Creating and starting MongoDB container '$(MONGO_CONTAINER_NAME)'..."; \ |
| 107 | + docker run -d --name $(MONGO_CONTAINER_NAME) -p 127.0.0.1:$(MONGO_PORT):27017 $(MONGO_IMAGE); \ |
| 108 | + $(MAKE) --no-print-directory mongo-ping; \ |
| 109 | + fi |
| 110 | +.PHONY: mongo-start |
| 111 | + |
| 112 | +mongo-stop: ## Stop and remove MongoDB container |
| 113 | + @if docker ps --format '{{.Names}}' | grep -q "^$(MONGO_CONTAINER_NAME)$$"; then \ |
| 114 | + echo "Stopping MongoDB container '$(MONGO_CONTAINER_NAME)'..."; \ |
| 115 | + docker stop $(MONGO_CONTAINER_NAME); \ |
| 116 | + docker rm $(MONGO_CONTAINER_NAME); \ |
| 117 | + echo "MongoDB container stopped and removed"; \ |
| 118 | + else \ |
| 119 | + if docker ps -a --format '{{.Names}}' | grep -q "^$(MONGO_CONTAINER_NAME)$$"; then \ |
| 120 | + echo "Removing stopped MongoDB container '$(MONGO_CONTAINER_NAME)'..."; \ |
| 121 | + docker rm $(MONGO_CONTAINER_NAME); \ |
| 122 | + else \ |
| 123 | + echo "MongoDB container '$(MONGO_CONTAINER_NAME)' is not running"; \ |
| 124 | + fi \ |
| 125 | + fi |
| 126 | +.PHONY: mongo-stop |
| 127 | + |
| 128 | +mongo-status: ## Check MongoDB container status |
| 129 | + @if docker ps --format '{{.Names}}' | grep -q "^$(MONGO_CONTAINER_NAME)$$"; then \ |
| 130 | + echo "✓ MongoDB container '$(MONGO_CONTAINER_NAME)' is running"; \ |
| 131 | + docker ps --filter "name=$(MONGO_CONTAINER_NAME)" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"; \ |
| 132 | + else \ |
| 133 | + if docker ps -a --format '{{.Names}}' | grep -q "^$(MONGO_CONTAINER_NAME)$$"; then \ |
| 134 | + echo "✗ MongoDB container '$(MONGO_CONTAINER_NAME)' exists but is not running"; \ |
| 135 | + else \ |
| 136 | + echo "✗ MongoDB container '$(MONGO_CONTAINER_NAME)' does not exist"; \ |
| 137 | + fi; \ |
| 138 | + exit 1; \ |
| 139 | + fi |
| 140 | +.PHONY: mongo-status |
| 141 | + |
| 142 | +mongo-ping: ## Wait for MongoDB to be ready |
| 143 | + @echo "Waiting for MongoDB to be ready..." |
| 144 | + @for i in 1 2 3 4 5 6 7 8 9 10; do \ |
| 145 | + if docker exec $(MONGO_CONTAINER_NAME) mongosh --quiet --eval "db.runCommand({ ping: 1 })" >/dev/null 2>&1; then \ |
| 146 | + echo "✓ MongoDB is ready"; \ |
| 147 | + exit 0; \ |
| 148 | + fi; \ |
| 149 | + echo " Attempt $$i/10: MongoDB not ready yet, waiting..."; \ |
| 150 | + sleep 1; \ |
| 151 | + done; \ |
| 152 | + echo "✗ MongoDB failed to start within 10 seconds"; \ |
| 153 | + exit 1 |
| 154 | +.PHONY: mongo-ping |
| 155 | + |
| 156 | +_mongo_ensure_running: ## Internal: ensure MongoDB is running |
| 157 | + @if ! docker ps --format '{{.Names}}' | grep -q "^$(MONGO_CONTAINER_NAME)$$"; then \ |
| 158 | + echo "MongoDB is not running. Starting MongoDB container..."; \ |
| 159 | + $(MAKE) --no-print-directory mongo-start; \ |
| 160 | + fi |
| 161 | +.PHONY: _mongo_ensure_running |
| 162 | + |
| 163 | +_mongo_cleanup: ## Internal: cleanup MongoDB after tests |
| 164 | + @trap '$(MAKE) --no-print-directory mongo-stop' EXIT; \ |
| 165 | + $(MAKE) --no-print-directory _run_tests ARGS="$(ARGS)" |
| 166 | +.PHONY: _mongo_cleanup |
| 167 | + |
| 168 | +_run_tests: clean | _prerequire _venv ## Internal: run the actual tests |
| 169 | + @args="$(filter-out $@,$(MAKECMDGOALS))" && \ |
| 170 | + arg_2="$${args:-${ARGS}}" && \ |
| 171 | + arg_3="$${arg_2:=$(ROOT_DIR)/nau_openedx_extensions}" && \ |
| 172 | + cd ${EDX_PLATFORM_PATH} && \ |
| 173 | + PYTHONPATH=${EDX_PLATFORM_PATH}:$(ROOT_DIR) \ |
| 174 | + EDXAPP_TEST_MONGO_HOST=localhost \ |
| 175 | + EDXAPP_TEST_MONGO_PORT=$(MONGO_PORT) \ |
| 176 | + $(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} |
| 177 | + @cd ${EDX_PLATFORM_PATH} && $(COVERAGE) combine || true |
| 178 | + @cd ${EDX_PLATFORM_PATH} && $(COVERAGE) report --include="$(ROOT_DIR)/nau_openedx_extensions/*" --fail-under=5 |
| 179 | + @echo "✓ Tests passed" |
| 180 | +.PHONY: _run_tests |
| 181 | + |
| 182 | +test: _prerequire ## Run tests with auto-managed MongoDB (starts, runs tests, stops). Usage: make test `pwd`/[path/to/test_file.py::TestClass::test_method] |
| 183 | + @$(MAKE) --no-print-directory mongo-start |
| 184 | + @cleanup() { $(MAKE) --no-print-directory mongo-stop; }; \ |
| 185 | + trap cleanup EXIT INT TERM; \ |
| 186 | + args="$(filter-out $@,$(MAKECMDGOALS))"; \ |
| 187 | + $(MAKE) --no-print-directory _run_tests ARGS="$$args" |
39 | 188 | .PHONY: test |
40 | 189 |
|
41 | | -lint: ## Run linters to check code style |
42 | | - $(TOX) pylint ./nau_openedx_extensions |
43 | | - $(TOX) pycodestyle ./nau_openedx_extensions |
44 | | - $(TOX) isort --check-only --diff ./nau_openedx_extensions |
| 190 | +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] |
| 191 | + @$(MAKE) --no-print-directory _mongo_ensure_running |
| 192 | + @args="$(filter-out $@,$(MAKECMDGOALS))" && \ |
| 193 | + $(MAKE) --no-print-directory _run_tests ARGS="$$args" |
| 194 | +.PHONY: test-keep-mongo |
| 195 | + |
| 196 | +lint: | _prerequire _venv ## Run linters to check code style |
| 197 | + @cd ${EDX_PLATFORM_PATH} && PYTHONPATH=${EDX_PLATFORM_PATH}:$(ROOT_DIR) $(VENV_BIN)/pylint $(ROOT_DIR)/nau_openedx_extensions |
| 198 | + @cd ${EDX_PLATFORM_PATH} && PYTHONPATH=${EDX_PLATFORM_PATH}:$(ROOT_DIR) $(VENV_BIN)/pycodestyle $(ROOT_DIR)/nau_openedx_extensions |
| 199 | + @cd ${EDX_PLATFORM_PATH} && PYTHONPATH=${EDX_PLATFORM_PATH}:$(ROOT_DIR) $(VENV_BIN)/isort --check-only --diff $(ROOT_DIR)/nau_openedx_extensions |
| 200 | + @echo "✓ Linting passed" |
45 | 201 | .PHONY: lint |
46 | 202 |
|
47 | | -lint-fix: ## Fix Python import sort |
48 | | - $(TOX) isort ./nau_openedx_extensions |
49 | | - $(TOX) autopep8 --in-place --aggressive --aggressive ./nau_openedx_extensions/*.py |
| 203 | +lint-fix: | _prerequire _venv ## Fix Python import sort |
| 204 | + @cd ${EDX_PLATFORM_PATH} && PYTHONPATH=${EDX_PLATFORM_PATH}:$(ROOT_DIR) $(VENV_BIN)/isort $(ROOT_DIR)/nau_openedx_extensions |
| 205 | + @cd ${EDX_PLATFORM_PATH} && PYTHONPATH=${EDX_PLATFORM_PATH}:$(ROOT_DIR) $(VENV_BIN)/autopep8 --in-place --aggressive --aggressive $(ROOT_DIR)/nau_openedx_extensions/*.py |
| 206 | + @echo "✓ Linting fixes applied" |
50 | 207 | .PHONY: lint-fix |
51 | 208 |
|
52 | 209 | # Define PIP_COMPILE_OPTS=-v to get more information during make upgrade. |
@@ -81,7 +238,7 @@ compile-requirements: pre-requirements ## Re-compile *.in requirements to *.txt |
81 | 238 | .PHONY: compile-requirements |
82 | 239 |
|
83 | 240 | upgrade: ## update the pip requirements files to use the latest releases satisfying our constraints |
84 | | - $(MAKE) compile-requirements COMPILE_OPTS="--upgrade" |
| 241 | + $(MAKE) --no-print-directory compile-requirements COMPILE_OPTS="--upgrade" |
85 | 242 | .PHONY: upgrade |
86 | 243 |
|
87 | 244 | # TODO: define somewhere else |
|
0 commit comments