diff --git a/.travis.yml b/.travis.yml index bdc0aad06c..d3ac736a7c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,16 +12,17 @@ addons: before_install: - sudo apt-get update - sudo apt-get install gettext - - make install - - make migrate + - pip3 install invoke + - invoke install + - invoke migrate - cd InvenTree && python3 manage.py createsuperuser --username InvenTreeAdmin --email admin@inventree.com --noinput && cd .. script: - cd InvenTree && python3 manage.py makemigrations && cd .. - python3 ci/check_migration_files.py - - make coverage - - make translate - - make style + - invoke coverage + - invoke translate + - invoke style after_success: - coveralls \ No newline at end of file diff --git a/InvenTree/setup.py b/InvenTree/setup.py deleted file mode 100644 index b74cfe7358..0000000000 --- a/InvenTree/setup.py +++ /dev/null @@ -1,68 +0,0 @@ -""" -Performs initial setup functions. - -- Generates a Django SECRET_KEY file to be used by manage.py -- Copies config template file (if a config file does not already exist) -""" - -import random -import string -import os -import sys -import argparse -from shutil import copyfile - -OUTPUT_DIR = os.path.dirname(os.path.realpath(__file__)) - -KEY_FN = 'secret_key.txt' -CONFIG_FN = 'config.yaml' -CONFIG_TEMPLATE_FN = 'config_template.yaml' - - -def generate_key(length=50): - """ Generate a random string - - Args: - length: Number of characters in returned string (default = 50) - - Returns: - Randomized secret key string - """ - - options = string.digits + string.ascii_letters + string.punctuation - key = ''.join([random.choice(options) for i in range(length)]) - return key - - -if __name__ == '__main__': - - parser = argparse.ArgumentParser(description='Generate Django SECRET_KEY file') - parser.add_argument('--force', '-f', help='Override existing files', action='store_true') - parser.add_argument('--dummy', '-d', help='Dummy run (do not create any files)', action='store_true') - - args = parser.parse_args() - - # Places to store files - key_filename = os.path.join(OUTPUT_DIR, KEY_FN) - conf_template = os.path.join(OUTPUT_DIR, CONFIG_TEMPLATE_FN) - conf_filename = os.path.join(OUTPUT_DIR, CONFIG_FN) - - # Generate secret key data - key_data = generate_key() - - if args.dummy: - print('SECRET_KEY: {k}'.format(k=key_data)) - sys.exit(0) - - if not args.force and os.path.exists(key_filename): - print("Key file already exists - '{f}'".format(f=key_filename)) - else: - with open(key_filename, 'w') as key_file: - print("Generating SECRET_KEY file - '{f}'".format(f=key_filename)) - key_file.write(key_data) - - if not args.force and os.path.exists(conf_filename): - print("Config file already exists (skipping)") - else: - print("Copying config template to 'config.yaml'") - copyfile(conf_template, conf_filename) diff --git a/Makefile b/Makefile deleted file mode 100644 index cc3a3043a6..0000000000 --- a/Makefile +++ /dev/null @@ -1,75 +0,0 @@ -clean: - find . -path '*/__pycache__/*' -delete - find . -type d -name '__pycache__' -empty -delete - find . -name *.pyc -o -name *.pyo -delete - rm -rf *.egg-info - rm -rf .cache - rm -rf .tox - rm -f .coverage - -update: install migrate static - -# Perform database migrations (after schema changes are made) -migrate: - cd InvenTree && python3 manage.py makemigrations - cd InvenTree && python3 manage.py migrate - cd InvenTree && python3 manage.py migrate --run-syncdb - cd InvenTree && python3 manage.py check - -# Collect static files into the correct locations -static: - cd InvenTree && python3 manage.py collectstatic - -# Install all required packages -install: - pip3 install -U -r requirements.txt - cd InvenTree && python3 setup.py - -# Create a superuser account -superuser: - cd InvenTree && python3 manage.py createsuperuser - -# Install pre-requisites for mysql setup -mysql: - sudo apt-get install mysql-server libmysqlclient-dev - pip3 install mysqlclient - -# Install pre-requisites for postgresql setup -postgresql: - sudo apt-get install postgresql postgresql-contrib libpq-dev - pip3 install psycopg2 - -# Update translation files -translate: - cd InvenTree && python3 manage.py makemessages - cd InvenTree && python3 manage.py compilemessages - -# Run PEP style checks against source code -style: - flake8 InvenTree - -# Run unit tests -test: - cd InvenTree && python3 manage.py check - cd InvenTree && python3 manage.py test barcode build common company label order part report stock InvenTree - -# Run code coverage -coverage: - cd InvenTree && python3 manage.py check - coverage run InvenTree/manage.py test barcode build common company label order part report stock InvenTree - coverage html - -# Install packages required to generate code docs -docreqs: - pip3 install -U -r docs/requirements.txt - -# Build code docs -docs: - cd docs && make html - -# Make database backup -backup: - cd InvenTree && python3 manage.py dbbackup - cd InvenTree && python3 manage.py mediabackup - -.PHONY: clean migrate superuser install mysql postgresql translate static style test coverage docreqs docs backup update \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 354c7ea316..3687341166 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,7 +17,7 @@ django-import-export==2.0.0 # Data import / export for admin interface django-cleanup==4.0.0 # Manage deletion of old / unused uploaded files django-qr-code==1.2.0 # Generate QR codes flake8==3.8.3 # PEP checking -coverage==4.0.3 # Unit test coverage +coverage==5.2.1 # Unit test coverage python-coveralls==2.9.1 # Coveralls linking (for Travis) rapidfuzz==0.7.6 # Fuzzy string matching django-stdimage==5.1.1 # Advanced ImageField management diff --git a/tasks.py b/tasks.py new file mode 100644 index 0000000000..3d92cec5c4 --- /dev/null +++ b/tasks.py @@ -0,0 +1,254 @@ +# -*- coding: utf-8 -*- + +from invoke import task +from shutil import copyfile + +import random +import string +import os + +def apps(): + """ + Returns a list of installed apps + """ + + return [ + 'barcode', + 'build', + 'common', + 'company', + 'label', + 'order', + 'part', + 'report', + 'stock', + 'InvenTree' + ] + +def localDir(): + """ + Returns the directory of *THIS* file. + Used to ensure that the various scripts always run + in the correct directory. + """ + return os.path.dirname(os.path.abspath(__file__)) + +def managePyDir(): + """ + Returns the directory of the manage.py file + """ + + return os.path.join(localDir(), 'InvenTree') + +def managePyPath(): + """ + Return the path of the manage.py file + """ + + return os.path.join(managePyDir(), 'manage.py') + +def manage(c, cmd): + """ + Runs a given command against django's "manage.py" script. + + Args: + c - Command line context + cmd - django command to run + """ + + c.run('cd {path} && python3 manage.py {cmd}'.format( + path=managePyDir(), + cmd=cmd + )) + +@task(help={'length': 'Length of secret key (default=50)'}) +def key(c, length=50, force=False): + """ + Generates a SECRET_KEY file which InvenTree uses for generating security hashes + """ + + SECRET_KEY_FILE = os.path.join(localDir(), 'InvenTree', 'secret_key.txt') + + # If a SECRET_KEY file does not exist, generate a new one! + if force or not os.path.exists(SECRET_KEY_FILE): + print("Generating SECRET_KEY file - " + SECRET_KEY_FILE) + with open(SECRET_KEY_FILE, 'w') as key_file: + options = string.digits + string.ascii_letters + string.punctuation + + key = ''.join([random.choice(options) for i in range(length)]) + + key_file.write(key) + + else: + print("SECRET_KEY file already exists - skipping") + + +@task(post=[key]) +def install(c): + """ + Installs required python packages, and runs initial setup functions. + """ + + # Install required Python packages with PIP + c.run('pip3 install -U -r requirements.txt') + + # If a config.yaml file does not exist, copy from the template! + CONFIG_FILE = os.path.join(localDir(), 'InvenTree', 'config.yaml') + CONFIG_TEMPLATE_FILE = os.path.join(localDir(), 'InvenTree', 'config_template.yaml') + + if not os.path.exists(CONFIG_FILE): + print("Config file 'config.yaml' does not exist - copying from template.") + copyfile(CONFIG_TEMPLATE_FILE, CONFIG_FILE) + +@task +def superuser(c): + """ + Create a superuser (admin) account for the database. + """ + + manage(c, 'createsuperuser') + +@task +def migrate(c): + """ + Performs database migrations. + This is a critical step if the database schema have been altered! + """ + + print("Running InvenTree database migrations...") + print("========================================") + + manage(c, "makemigrations") + manage(c, "migrate") + manage(c, "migrate --run-syncdb") + manage(c, "check") + + print("========================================") + print("InvenTree database migrations completed!") + + +@task +def static(c): + """ + Copies required static files to the STATIC_ROOT directory, + as per Django requirements. + """ + + manage(c, "collectstatic") + + +@task(pre=[install, migrate, static]) +def update(c): + """ + Update InvenTree installation. + + This command should be invoked after source code has been updated, + e.g. downloading new code from GitHub. + + The following tasks are performed, in order: + + - install + - migrate + - static + """ + pass + +@task +def translate(c): + """ + Regenerate translation files. + + Run this command after added new translatable strings, + or after adding translations for existing strings. + """ + + manage(c, "makemigrations") + manage(c, "compilemessages") + +@task +def style(c): + """ + Run PEP style checks against InvenTree sourcecode + """ + + print("Running PEP style checks...") + c.run('flake8 InvenTree') + +@task +def test(c): + """ + Run unit-tests for InvenTree codebase. + """ + + # Run sanity check on the django install + manage(c, 'check') + + # Run coverage tests + manage(c, 'test {apps}'.format( + apps=' '.join(apps()) + )) + +@task +def coverage(c): + """ + Run code-coverage of the InvenTree codebase, + using the 'coverage' code-analysis tools. + + Generates a code coverage report (available in the htmlcov directory) + """ + + # Run sanity check on the django install + manage(c, 'check') + + # Run coverage tests + c.run('coverage run {manage} test {apps}'.format( + manage=managePyPath(), + apps=' '.join(apps()) + )) + + # Generate coverage report + c.run('coverage html') + +@task +def mysql(c): + """ + Install packages required for using InvenTree with a MySQL database. + """ + + print('Installing packages required for MySQL') + + c.run('sudo apt-get install mysql-server libmysqlclient-dev') + c.run('pip3 install mysqlclient') + +@task +def postgresql(c): + """ + Install packages required for using InvenTree with a PostgreSQL database + """ + + print("Installing packages required for PostgreSQL") + + c.run('sudo apt-get install postgresql postgresql-contrib libpq-dev') + c.run('pip3 install psycopg2') + +@task +def backup(c): + """ + Create a backup of database models and uploaded media files. + + Backup files will be written to the 'backup_dir' file specified in 'config.yaml' + + """ + + manage(c, 'dbbackup') + manage(c, 'mediabackup') + +@task(help={'address': 'Server address:port (default=127.0.0.1:8000)'}) +def server(c, address="127.0.0.1:8000"): + """ + Launch a (deveopment) server using Django's in-built webserver. + + Note: This is *not* sufficient for a production installation. + """ + + manage(c, "runserver {address}".format(address=address))