Skip to content

Commit

Permalink
feat: olive updates, more scaffolding, and basic 'make test'
Browse files Browse the repository at this point in the history
* Replace COMMANDS_INIT (which is deprecated in Olive) with
  equivalent usage of CLI_DO_INIT_TASKS (which is new in Olive).
  Closes overhangio#15.

* Add boilerplate & explanation for CLI_DO_COMMANDS (new in Olive),
  include a dummy example `tutor local do say-hi` job.

* Add boilerplate and explanation for CLI_COMMANDS, including
  a dummy example `tutor myplugin print-repo` command.

* Add Makefile to both cookiecutter root as well as generated plugin,
  providing make rules `test`, `test-format`, `test-lint`, and
  `test-types`. They are not yet run in CI.
  Part of overhangio#7.
  • Loading branch information
kdmccormick committed Dec 7, 2022
1 parent 72ee1e5 commit c1fd177
Show file tree
Hide file tree
Showing 8 changed files with 148 additions and 5 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/tutor-contrib-myplugin
26 changes: 26 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
.PHONY: generate-plugin-for-tests help test test-plugin test-plugin-install \
test-plugin-quality
.DEFAULT_GOAL := help

test: generate-plugin-for-tests test-plugin

generate-plugin-for-tests:
rm -rf tutor-contrib-myplugin
cookiecutter --no-input .

test-plugin: test-plugin-quality test-plugin-install

test-plugin-quality:
cd tutor-contrib-myplugin && make test

test-plugin-install:
pip install -e tutor-contrib-myplugin
tutor plugins enable myplugin
tutor config save
tutor myplugin print-repo # This should just print a line and exit 0.

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}'
27 changes: 27 additions & 0 deletions {{ cookiecutter.package_name }}/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
.DEFAULT_GOAL := help
.PHONY: docs
SRC_DIRS = ./{{ cookiecutter.module_name }}
BLACK_OPTS = --exclude templates ${SRC_DIRS}

test: test-lint test-types test-format # Runs checks, all of which are static. These no CI check for any of these (yet... to be fixed in https://github.com/overhangio/cookiecutter-tutor-plugin/issues/7).

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}

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}'
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
from __future__ import annotations

from glob import glob
import os
import os.path
import pkg_resources
import shlex

import click
from tutor import hooks

from .__about__ import __version__
Expand Down Expand Up @@ -45,11 +50,27 @@
# INITIALIZATION TASKS
########################################

# To run the script from templates/{{ cookiecutter.plugin_name }}/tasks/myservice/init, add:
# hooks.Filters.COMMANDS_INIT.add_item((
# "myservice",
# ("{{ cookiecutter.plugin_name }}", "tasks", "myservice", "init"),
# ))
# To add a custom initialization task, create a bash script template under:
# {{ cookiecutter.module_name }}/templates/{{ cookiecutter.plugin_name }}/jobs/init/
# 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, ...]]] = [
("lms", ("{{ cookiecutter.plugin_name }}", "jobs", "init", "lms.sh")),
("cms", ("{{ cookiecutter.plugin_name }}", "jobs", "init", "cms.sh")),
("mysql", ("{{ cookiecutter.plugin_name }}", "jobs", "init", "mysql.sh")),
]


# For each task added to MY_INIT_TASKS, we load the task template
# and add it to the CLI_DO_INIT_TASKS filter, which tells Tutor to
# run it as part of the `init` job.
for service, template_path in MY_INIT_TASKS:
full_path = pkg_resources.resource_filename(
"{{ cookiecutter.module_name }}", os.path.join("templates", *template_path)
)
with open(full_path, encoding="utf-8") as init_task_file:
init_task = shlex.join(["bash", "-c", init_task_file.read()])
hooks.Filters.CLI_DO_INIT_TASKS.add_item((service, init_task))


########################################
Expand Down Expand Up @@ -117,3 +138,68 @@
):
with open(path, encoding="utf-8") as patch_file:
hooks.Filters.ENV_PATCHES.add_item((os.path.basename(path), patch_file.read()))


########################################
# CUSTOM JOBS (a.k.a. "do-commands")
########################################

# A job is a set of tasks, each of which run inside a certain container.
# Jobs are invoked using the `do` command, for example: `tutor local do importdemocourse`.
# A few jobs are built in to Tutor, such as `init` and `createuser`.
# You can also add your own custom jobs:

# To add a custom job, define a Click command that returns a list of tasks,
# where each task is a pair in the form ("<service>", "<shell_command>").
# For example:
@click.command()
@click.option("-n", "name", default="plugin developer")
def say_hi(name: str) -> list[tuple[str, str]]:
"""
An example job that just prints 'hello' from within both LMS and CMS.
"""
return [
("lms", f"echo 'Hello from LMS, {name}!'"),
("cms", f"echo 'Hello from CMS, {name}!'"),
]


# Then, add the command function to CLI_DO_COMMANDS:
hooks.Filters.CLI_DO_COMMANDS.add_item(say_hi)
#
# Now, you can run your job like this:
# $ tutor local do say-hi --name="{{ cookiecutter.author }}"


#######################################
# CUSTOM CLI COMMANDS
#######################################

# Your plugin can also add custom commands directly to the Tutor CLI.
# These commands are run directly on the user's host computer
# (unlike jobs, which are run in containers).

# To define a command group for your plugin, define a Click group and then
# add it to CLI_COMMANDS:


@click.group()
def {{ cookiecutter.plugin_name }}() -> None:
pass


hooks.Filters.CLI_COMMANDS.add_item({{ cookiecutter.plugin_name }})

# Then, add subcommands directly to the Click group, for example:


@{{ cookiecutter.plugin_name }}.command()
def print_repo() -> None:
"""
An example command that just prints out this plugin's git repo URL.
"""
print("{{ cookiecutter.git_repo }}")


# And run:
# $ tutor {{ cookiecutter.plugin_name }} print-repo
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Add your CMS initialization commands here.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Add your LMS initialization commands here.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Add your MySQL initialization commands here.
Empty file.

0 comments on commit c1fd177

Please sign in to comment.