Feat: Testing and Linting (#31)

* ci: test action added

* chore: changelog entry added

* feat: Makefile added

* fix: typing added

* chore: cleanup with isort and black
This commit is contained in:
Emad Rad 2023-11-24 02:35:41 +03:30 committed by GitHub
parent 16012432a1
commit d4a4274bc4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 95 additions and 37 deletions

22
.github/workflows/test.yml vendored Normal file
View File

@ -0,0 +1,22 @@
name: Run tests
on:
pull_request:
branches: [master]
jobs:
tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.8
uses: actions/setup-python@v2
with:
python-version: 3.8
- name: Upgrade pip
run: python -m pip install --upgrade pip setuptools
- name: Install dependencies
run: |
pip install 'tutor[dev]>=16.0.0,<17.0.0'
- name: Test lint, types, and format
run: make test

34
Makefile Normal file
View File

@ -0,0 +1,34 @@
.DEFAULT_GOAL := help
.PHONY: docs
SRC_DIRS = ./tutornotes
BLACK_OPTS = --exclude templates ${SRC_DIRS}
# Warning: These checks are run on every PR.
test: test-lint test-types test-format # Run some static checks.
test-format: ## Run code formatting tests.
black --check --diff $(BLACK_OPTS)
test-lint: ## Run code linting tests
pylint --errors-only --enable=unused-import,unused-argument --ignore=templates --ignore=docs/_ext ${SRC_DIRS}
test-types: ## Run type checks.
mypy --exclude=templates --ignore-missing-imports --implicit-reexport --strict ${SRC_DIRS}
format: ## Format code automatically.
black $(BLACK_OPTS)
isort: ## Sort imports. This target is not mandatory because the output may be incompatible with black formatting. Provided for convenience purposes.
isort --skip=templates ${SRC_DIRS}
changelog-entry: ## Create a new changelog entry.
scriv create
changelog: ## Collect changelog entries in the CHANGELOG.md file.
scriv collect
ESCAPE = 
help: ## Print this help.
@grep -E '^([a-zA-Z_-]+:.*?## .*|######* .+)$$' Makefile \
| sed 's/######* \(.*\)/@ $(ESCAPE)[1;31m\1$(ESCAPE)[0m/g' | tr '@' '\n' \
| awk 'BEGIN {FS = ":.*?## "}; {printf "\033[33m%-30s\033[0m %s\n", $$1, $$2}'

View File

@ -0,0 +1 @@
- [Improvement] Added Makefile and test action to repository and formatted code with Black and isort. (by @CodeWithEmad)

View File

@ -1,11 +1,10 @@
from __future__ import annotations from __future__ import annotations
from glob import glob
import os import os
import typing as t import typing as t
from glob import glob
import pkg_resources import pkg_resources
from tutor import hooks as tutor_hooks from tutor import hooks as tutor_hooks
from tutor.__about__ import __version_suffix__ from tutor.__about__ import __version_suffix__
@ -16,11 +15,6 @@ if __version_suffix__:
__version__ += "-" + __version_suffix__ __version__ += "-" + __version_suffix__
config = { config = {
"unique": {
"MYSQL_PASSWORD": "{{ 8|random_string }}",
"SECRET_KEY": "{{ 24|random_string }}",
"OAUTH2_SECRET": "{{ 24|random_string }}",
},
"defaults": { "defaults": {
"VERSION": __version__, "VERSION": __version__,
"DOCKER_IMAGE": "{{ DOCKER_REGISTRY }}overhangio/openedx-notes:{{ NOTES_VERSION }}", "DOCKER_IMAGE": "{{ DOCKER_REGISTRY }}overhangio/openedx-notes:{{ NOTES_VERSION }}",
@ -30,14 +24,14 @@ config = {
"REPOSITORY": "https://github.com/openedx/edx-notes-api", "REPOSITORY": "https://github.com/openedx/edx-notes-api",
"REPOSITORY_VERSION": "{{ OPENEDX_COMMON_VERSION }}", "REPOSITORY_VERSION": "{{ OPENEDX_COMMON_VERSION }}",
}, },
"unique": {
"MYSQL_PASSWORD": "{{ 8|random_string }}",
"SECRET_KEY": "{{ 24|random_string }}",
"OAUTH2_SECRET": "{{ 24|random_string }}",
},
} }
# Initialization hooks # Initialization hooks
# To add a custom initialization task, create a bash script template under:
# tutorcodejail/templates/codejail/tasks/
# and then add it to the MY_INIT_TASKS list. Each task is in the format:
# ("<service>", ("<path>", "<to>", "<script>", "<template>"))
MY_INIT_TASKS: list[tuple[str, tuple[str, ...]]] = [ MY_INIT_TASKS: list[tuple[str, tuple[str, ...]]] = [
("mysql", ("notes", "tasks", "mysql", "init")), ("mysql", ("notes", "tasks", "mysql", "init")),
("lms", ("notes", "tasks", "lms", "init")), ("lms", ("notes", "tasks", "lms", "init")),
@ -56,23 +50,32 @@ for service, template_path in MY_INIT_TASKS:
tutor_hooks.Filters.CLI_DO_INIT_TASKS.add_item((service, init_task)) tutor_hooks.Filters.CLI_DO_INIT_TASKS.add_item((service, init_task))
# Image management # Image management
tutor_hooks.Filters.IMAGES_BUILD.add_item(( tutor_hooks.Filters.IMAGES_BUILD.add_item(
(
"notes", "notes",
("plugins", "notes", "build", "notes"), ("plugins", "notes", "build", "notes"),
"{{ NOTES_DOCKER_IMAGE }}", "{{ NOTES_DOCKER_IMAGE }}",
(), (),
)) )
tutor_hooks.Filters.IMAGES_PULL.add_item(( )
tutor_hooks.Filters.IMAGES_PULL.add_item(
(
"notes", "notes",
"{{ NOTES_DOCKER_IMAGE }}", "{{ NOTES_DOCKER_IMAGE }}",
)) )
tutor_hooks.Filters.IMAGES_PUSH.add_item(( )
tutor_hooks.Filters.IMAGES_PUSH.add_item(
(
"notes", "notes",
"{{ NOTES_DOCKER_IMAGE }}", "{{ NOTES_DOCKER_IMAGE }}",
)) )
)
@tutor_hooks.Filters.COMPOSE_MOUNTS.add() @tutor_hooks.Filters.COMPOSE_MOUNTS.add()
def _mount_edx_notes_api(volumes, name): def _mount_edx_notes_api(
volumes: list[tuple[str, str]], name: str
) -> list[tuple[str, str]]:
""" """
When mounting edx-notes-api with `--mount=/path/to/edx-notes-api`, When mounting edx-notes-api with `--mount=/path/to/edx-notes-api`,
bind-mount the host repo in the notes container. bind-mount the host repo in the notes container.
@ -85,7 +88,7 @@ def _mount_edx_notes_api(volumes, name):
] ]
return volumes return volumes
####### Boilerplate code
# Add the "templates" folder as a template root # Add the "templates" folder as a template root
tutor_hooks.Filters.ENV_TEMPLATE_ROOTS.add_item( tutor_hooks.Filters.ENV_TEMPLATE_ROOTS.add_item(
pkg_resources.resource_filename("tutornotes", "templates") pkg_resources.resource_filename("tutornotes", "templates")
@ -108,26 +111,24 @@ for path in glob(
tutor_hooks.Filters.ENV_PATCHES.add_item( tutor_hooks.Filters.ENV_PATCHES.add_item(
(os.path.basename(path), patch_file.read()) (os.path.basename(path), patch_file.read())
) )
# Add configuration entries # Add configuration entries
tutor_hooks.Filters.CONFIG_DEFAULTS.add_items( tutor_hooks.Filters.CONFIG_DEFAULTS.add_items(
[ [(f"NOTES_{key}", value) for key, value in config.get("defaults", {}).items()]
(f"NOTES_{key}", value)
for key, value in config.get("defaults", {}).items()
]
) )
tutor_hooks.Filters.CONFIG_UNIQUE.add_items( tutor_hooks.Filters.CONFIG_UNIQUE.add_items(
[ [(f"NOTES_{key}", value) for key, value in config.get("unique", {}).items()]
(f"NOTES_{key}", value)
for key, value in config.get("unique", {}).items()
]
) )
tutor_hooks.Filters.CONFIG_OVERRIDES.add_items( tutor_hooks.Filters.CONFIG_OVERRIDES.add_items(
list(config.get("overrides", {}).items()) list(config.get("overrides", {}).items())
) )
# Notes public hosts # Notes public hosts
@tutor_hooks.Filters.APP_PUBLIC_HOSTS.add() @tutor_hooks.Filters.APP_PUBLIC_HOSTS.add()
def _notes_public_hosts(hosts: list[str], context_name: t.Literal["local", "dev"]) -> list[str]: def _notes_public_hosts(
hosts: list[str], context_name: t.Literal["local", "dev"]
) -> list[str]:
if context_name == "dev": if context_name == "dev":
hosts += ["{{ NOTES_HOST }}:8120"] hosts += ["{{ NOTES_HOST }}:8120"]
else: else: