diff --git a/fabfile.py b/fabfile.py deleted file mode 100644 index e18a34d06..000000000 --- a/fabfile.py +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -import webbrowser - -from fabric.api import local -from fabric.contrib import django - - -def test(module='tests'): - """ - Runs all unit tests for OpenSlides using coverage. - - The settings file in the tests directory is used, therefor the - environment variable DJANGO_SETTINGS_MODULE is set to 'tests.settings'. - """ - django.settings_module('tests.settings') - local('coverage run ./manage.py django test %s' % module) - - -def coverage_report_plain(): - """ - Runs all tests and prints the coverage report. - """ - test() - local('coverage report -m --fail-under=80') - - -def coverage(): - """ - Runs all tests and builds the coverage html files. - - The index of these files is opened in the webbrowser in the end. - """ - test() - local('coverage html') - webbrowser.open(os.path.join(os.path.dirname(__file__), 'htmlcov', 'index.html')) - - -def check(): - """ - Checks for PEP 8 errors in openslides and in tests. - """ - local('flake8 --max-line-length=150 --statistics openslides tests') - - -def prepare_commit(): - """ - Does everything that should be done before a commit. - - At the moment it is running the tests and check for PEP 8 errors. - """ - test() - check() - - -def travis_ci(): - """ - Command that is run by Travis CI. - """ - coverage_report_plain() - check() - - -def run_script(script): - """ - Run a script with the development version of OpenSlides. - - You can find some usefull scripts in extras/scrips/ in the OpenSlides repo. - """ - os.environ['PYTHONPATH'] = os.path.join(os.path.dirname(__file__)) - os.system("python " + script) diff --git a/make/README.rst b/make/README.rst new file mode 100644 index 000000000..34d052d30 --- /dev/null +++ b/make/README.rst @@ -0,0 +1,13 @@ +================================== +Commands for OpenSlides developers +================================== + +In here are scripts that are useful for OpenSlides development. + +To call a command run: + + $ python make COMMAND + +To see a list of all commands run: + + $ python make --help diff --git a/make/__main__.py b/make/__main__.py new file mode 100644 index 000000000..690dee641 --- /dev/null +++ b/make/__main__.py @@ -0,0 +1,13 @@ +import sys + +import commands # noqa +from parser import parser + + +if len(sys.argv) < 2: + args = parser.parse_args(['--help']) +else: + args = parser.parse_args() + +# Runs the command end exits the script with the return-code of the command +exit(args.func(args)) diff --git a/make/commands.py b/make/commands.py new file mode 100644 index 000000000..236d3a27e --- /dev/null +++ b/make/commands.py @@ -0,0 +1,84 @@ +import re + +from parser import command, argument, call + + +@argument('module', nargs='?', default='') +@command('test', help='runs the tests') +def test(args=None): + """ + Runs the tests. + """ + module = getattr(args, 'module', '') + return call("DJANGO_SETTINGS_MODULE='tests.settings' coverage run " + "./manage.py django test %s" % module) + + +@argument('--plain', action='store_true') +@command('coverage', help='Runs all tests and builds the coverage html files') +def coverage(args=None, plain=None): + """ + Runs the tests and creates a coverage report. + + By default it creates a html report. With the argument --plain, it creates + a plain report and fails under a certain amount of untested lines. + """ + test() + if plain is None: + plain = getattr(args, 'plain', False) + if plain: + return call('coverage report -m --fail-under=80') + else: + return call('coverage html') + + +@command('check', help='Checks for pep8 errors in openslides and tests') +def check(args=None): + """ + Checks for pep8 and other code styling conventions. + """ + return call('flake8 --max-line-length=150 --statistics openslides tests') + + +@command('travis', help='Runs the code that travis does') +def travis(args=None): + """ + Runs all commands that travis tests. + """ + return_codes = [] + with open('.travis.yml') as f: + script_lines = False + for line in (line.strip() for line in f.readlines()): + if line == 'script:': + script_lines = True + continue + if not script_lines: + continue + + match = re.search(r'"(.*)"', line) + return_codes.append(call(match.group(1))) + + # Retuns True if one command exited with a different statuscode then 1 + return bool(list(filter(bool, return_codes))) + + +@argument('-r', '--requirements', nargs='?', + default='requirements_production.txt') +@command('min_requirements', + help='Prints a pip line to install the minimum supported versions of ' + 'the requirements.') +def min_requirements(args=None): + """ + Prints a pip install command to install the minimal supported versions of a + requirement file. + + Uses requirements_production.txt by default. + """ + + from pip.req import parse_requirements + + def get_lowest_versions(requirements_file): + for line in parse_requirements(requirements_file): + yield '%s==%s' % (line.req.key, line.req.specs[0][1]) + + print('pip install %s' % ' '.join(get_lowest_versions(args.requirements))) diff --git a/make/parser.py b/make/parser.py new file mode 100644 index 000000000..25fd46618 --- /dev/null +++ b/make/parser.py @@ -0,0 +1,49 @@ +from argparse import ArgumentParser +from subprocess import call as _call + +parser = ArgumentParser(description='Development scripts for OpenSlides') +subparsers = parser.add_subparsers() + + +def command(*args, **kwargs): + """ + Decorator to create a argparse command. + + The arguments to this decorator are used as arguments for the argparse + command. + """ + class decorator: + def __init__(self, func): + self.parser = subparsers.add_parser(*args, **kwargs) + self.parser.set_defaults(func=func) + self.func = func + + def __call__(self, *args, **kwargs): + return self.func(*args, **kwargs) + + return decorator + + +def argument(*args, **kwargs): + """ + Decorator to create arguments for argparse commands. + + The arguments to this decorator are used as arguments for the argparse + argument. + + Does only work if the decorated function was decorated with the + command-decorator before. + """ + def decorator(func): + func.parser.add_argument(*args, **kwargs) + def wrapper(*func_args, **func_kwargs): + return func(*func_args, **func_kwargs) + return wrapper + return decorator + + +def call(*args, **kwargs): + """ + Calls a command in a shell. + """ + return _call(shell=True, *args, **kwargs)