diff --git a/dev/bootstrap.py b/dev/bootstrap.py new file mode 100644 index 0000000..022333e --- /dev/null +++ b/dev/bootstrap.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8; -*- +""" +Bootstrap development for Corporal +""" + +import os +import subprocess +import sys + + +here = os.path.abspath(os.path.dirname(__file__)) + + +def bootstrap(): + if not inside_virtualenv(): + return + + # install wheel + subprocess.run(['pip', 'install', 'wheel'], + check=True) + + # install invoke, sphinx + subprocess.run(['pip', 'install', 'invoke', 'Sphinx'], + check=True) + + # run bootstrap task + os.chdir(here) + try: + if sys.platform == 'win32': + completed = subprocess.run(['invoke', 'bootstrap']) + else: + completed = subprocess.run(['invoke', '--echo', 'bootstrap']) + except KeyboardInterrupt: + sys.exit(130) # 128 + SIGINT + else: + sys.exit(completed.returncode) + + +def inside_virtualenv(): + if not (hasattr(sys, 'real_prefix') or + (hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix)): + print("") + print("Not running inside a virtual environment!") + print("") + print("Please create and activate that first, e.g. like:") + print("") + if sys.platform == 'win32': + print(" py -m venv C:\\envs\\corporal") + print(" C:\\envs\\corporal\\Scripts\\activate.bat") + else: + print(" python -m venv /srv/envs/corporal") + print(" source /srv/envs/corporal/bin/activate") + print("") + return False + return True + + +if __name__ == '__main__': + bootstrap() diff --git a/dev/rattail.conf b/dev/rattail.conf new file mode 100644 index 0000000..9280a68 --- /dev/null +++ b/dev/rattail.conf @@ -0,0 +1,146 @@ + +############################################################ +# +# Development config for Corporal +# +############################################################ + +[corepos] +office.url = http://localhost/fannie/ + +[corepos.api] +url = http://localhost/fannie/ws/ + +[corepos.db.office_op] +default.url = mysql+mysqlconnector://corepos:corepospass@localhost/core_op +default.pool_recycle = 3600 + +[corepos.db.office_trans] +default.url = mysql+mysqlconnector://corepos:corepospass@localhost/core_trans +default.pool_recycle = 3600 + + +############################## +# rattail +############################## + +[rattail] +app_title = Corporal +timezone.default = America/Chicago +datadir = appdata +batch.files = appdatabatch +workdir = appwork + +[rattail.config] +# include = /etc/rattail/rattail.conf +configure_logging = true +usedb = true +preferdb = true + +[rattail.db] +default.url = postgresql://:@/ +versioning.enabled = true + +[rattail.mail] +send_emails = false +smtp.server = localhost +templates = rattail:templates/mail +default.prefix = [Rattail] +default.from = rattail@localhost +default.to = root@localhost +# default.enabled = false + +[rattail.pod] +pictures.gtin.root_url = https://rattailproject.org/pod/pictures/gtin + +[rattail.upgrades] +files = appdataupgrades + +############################## +# alembic +############################## + +[alembic] +script_location = rattail.db:alembic +version_locations = rattail.db:alembic/versions + + +############################## +# logging +############################## + +[loggers] +keys = root, exc_logger, beaker, txn, sqlalchemy, django_db, flufl_bounce, requests + +[handlers] +keys = file, console, email + +[formatters] +keys = generic, console + +[logger_root] +handlers = file, console +level = DEBUG + +[logger_exc_logger] +qualname = exc_logger +handlers = email +level = ERROR + +[logger_beaker] +qualname = beaker +handlers = +level = INFO + +[logger_txn] +qualname = txn +handlers = +level = INFO + +[logger_sqlalchemy] +qualname = sqlalchemy.engine +handlers = +# handlers = file +# level = INFO + +[logger_django_db] +qualname = django.db.backends +handlers = +level = INFO +# level = DEBUG + +[logger_flufl_bounce] +qualname = flufl.bounce +handlers = +level = WARNING + +[logger_requests] +qualname = requests +handlers = +# level = WARNING + +[handler_file] +class = handlers.RotatingFileHandler +args = (r'applograttail.log', 'a', 1000000, 100, 'utf_8') +formatter = generic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +formatter = console +# formatter = generic +# level = INFO +# level = WARNING + +[handler_email] +class = handlers.SMTPHandler +args = ('localhost', 'rattail@localhost', ['root@localhost'], "[Rattail] Logging") +formatter = generic +level = ERROR + +[formatter_generic] +format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(funcName)s: %(message)s +datefmt = %Y-%m-%d %H:%M:%S + +[formatter_console] +format = %(levelname)-5.5s [%(name)s][%(threadName)s] %(funcName)s: %(message)s diff --git a/dev/settings.ini b/dev/settings.ini new file mode 100644 index 0000000..60b6278 --- /dev/null +++ b/dev/settings.ini @@ -0,0 +1,7 @@ +[devbootstrap] +dbhost = localhost +dbname = corporal +dbuser = rattail +dbpass = rattailpass +corporaluser = lance +corporalpass = lancepass diff --git a/dev/tasks.py b/dev/tasks.py new file mode 100644 index 0000000..78652bf --- /dev/null +++ b/dev/tasks.py @@ -0,0 +1,212 @@ +# -*- coding: utf-8; -*- +""" +Development Tasks for Corporal +""" + +import os +import configparser +import shutil +import sys + +from invoke import task +from sphinx.util.console import bold +from sphinx.cmd.quickstart import do_prompt + +from bootstrap import inside_virtualenv + + +here = os.path.abspath(os.path.dirname(__file__)) + + +@task +def bootstrap(c): + """ + Bootstrap a development environment. + """ + if not inside_virtualenv(): + sys.exit(1) + + envdir = sys.prefix + upgrade_pip(c) + install_app_package(c) + appdir = make_appdir(c, envdir) + + info = collect_info() + make_configs(c, envdir, appdir, info) + check_db(c, envdir, appdir) + install_db_schema(c, envdir, appdir) + make_admin_user(c, envdir, appdir, info) + + print() + print(bold("============================================================")) + print() + print(bold(" Okay, you should be ready to go!")) + print() + print(bold("============================================================")) + print() + print("start your development web app with this command:") + print() + print(" cd {}".format(envdir)) + if sys.platform == 'win32': + print(r" Scripts\pserve --reload file+ini:app\web.conf") + else: + print(" bin/pserve --reload file+ini:app/web.conf") + print() + print("then check out your development web app at:") + print() + print(" http://localhost:9080") + print() + + +def collect_info(): + """ + Collect misc. dev info from user + """ + info = {} + print() + print(bold("Welcome to Corporal, the CORE supplement.")) + + config = configparser.ConfigParser() + if config.read(os.path.join(here, 'settings.ini')): + if config.has_section('devbootstrap'): + info = dict(config.items('devbootstrap')) + + try: + print() + print("Please enter the details for your Corporal database.") + print() + info['dbhost'] = do_prompt('DB host', + default=info.get('dbhost', 'localhost')) + info['dbname'] = do_prompt('DB name', + default=info.get('dbname', 'corporal')) + info['dbuser'] = do_prompt('DB user', + default=info.get('dbuser', 'rattail')) + info['dbpass'] = do_prompt('DB password', + default=info.get('dbpass')) + + print() + print("Please enter the details for your Corporal admin user.") + print() + info['corporaluser'] = do_prompt('Corporal username', + default=info.get('corporaluser', 'admin')) + info['corporalpass'] = do_prompt('Corporal password', + default=info.get('corporalpass')) + + except (KeyboardInterrupt, EOFError): + print("\n[Interrupted.]") + sys.exit(130) # 128 + SIGINT + + return info + + +def upgrade_pip(c): + """ + Upgrade pip and friends + """ + if sys.platform == 'win32': + c.run('python -m pip install -U pip') + else: + c.run('pip install -U pip') + c.run('pip install -U setuptools wheel') + + +def install_app_package(c): + """ + Install the Corporal app package + """ + project = os.path.abspath(os.path.join(here, os.pardir)) + c.run('pip install -e {}'.format(project)) + # c.run('pip install Corporal') + + +def make_appdir(c, envdir): + """ + Create the 'app' dir for virtual env + """ + appdir = os.path.join(envdir, 'app') + if not os.path.exists(appdir): + if sys.platform == 'win32': + c.run('{} make-appdir --path {}'.format( + os.path.join(envdir, 'Scripts', 'rattail'), + appdir)) + else: + c.run('{}/bin/rattail make-appdir --path {}'.format( + envdir, appdir)) + return appdir + + +def make_configs(c, envdir, appdir, info): + """ + Create app config files + """ + # rattail.conf + if not os.path.exists(os.path.join(appdir, 'rattail.conf')): + with open('rattail.conf') as f: + contents = f.read() + contents = contents.replace('', envdir) + contents = contents.replace('', os.sep) + contents = contents.replace('', info['dbhost']) + contents = contents.replace('', info['dbname']) + contents = contents.replace('', info['dbuser']) + contents = contents.replace('', info['dbpass']) + with open(os.path.join(appdir, 'rattail.conf'), 'w') as f: + f.write(contents) + + # quiet.conf + if not os.path.exists(os.path.join(appdir, 'quiet.conf')): + if sys.platform == 'win32': + c.run('{} make-config -T quiet -O {}'.format( + os.path.join(envdir, 'Scripts', 'rattail'), + appdir)) + else: + c.run('{}/bin/rattail make-config -T quiet -O {}'.format( + envdir, appdir)) + + # web.conf + if not os.path.exists(os.path.join(appdir, 'web.conf')): + with open('web.conf') as f: + contents = f.read() + contents = contents.replace('', envdir) + contents = contents.replace('', os.sep) + with open(os.path.join(appdir, 'web.conf'), 'w') as f: + f.write(contents) + + +def check_db(c, envdir, appdir): + """ + Do basic sanity checks for Corporal database + """ + if sys.platform == 'win32': + c.run('{} -c {} --no-versioning checkdb'.format( + os.path.join(envdir, 'Scripts', 'rattail'), + os.path.join(appdir, 'quiet.conf'))) + else: + c.run('{}/bin/rattail -c {}/quiet.conf --no-versioning checkdb'.format( + envdir, appdir)) + + +def install_db_schema(c, envdir, appdir): + """ + Install the schema for Corporal database + """ + if sys.platform == 'win32': + c.run('{} -c {} upgrade heads'.format( + os.path.join(envdir, 'Scripts', 'alembic'), + os.path.join(appdir, 'rattail.conf'))) + else: + c.run('{}/bin/alembic -c {}/rattail.conf upgrade heads'.format( + envdir, appdir)) + + +def make_admin_user(c, envdir, appdir, info): + """ + Make an admin user in the Corporal database + """ + if sys.platform == 'win32': + c.run('{} -c {} make-user --admin {} --password {}'.format( + os.path.join(envdir, 'Scripts', 'rattail'), + os.path.join(appdir, 'quiet.conf'), + info['corporaluser'], info['corporalpass'])) + else: + c.run('{}/bin/rattail -c {}/quiet.conf make-user --admin {} --password {}'.format( + envdir, appdir, info['corporaluser'], info['corporalpass'])) diff --git a/dev/web.conf b/dev/web.conf new file mode 100644 index 0000000..47f7972 --- /dev/null +++ b/dev/web.conf @@ -0,0 +1,58 @@ + +############################################################ +# +# Development web app config for Corporal +# +############################################################ + + +############################## +# rattail +############################## + +[rattail.config] +include = %(here)srattail.conf + + +############################## +# pyramid +############################## + +[app:main] +use = egg:Corporal + +pyramid.reload_templates = true +pyramid.debug_all = true +pyramid.default_locale_name = en +# you can enable this if you find it helpful +#pyramid.includes = pyramid_debugtoolbar + +beaker.session.type = file +beaker.session.data_dir = %(here)s/sessions/data +beaker.session.lock_dir = %(here)s/sessions/lock +# this secret should only be used in development! +beaker.session.secret = b15zXlYNNIfzRjDjjyOL +beaker.session.key = rattail + +pyramid_deform.tempdir = %(here)s/data/uploads + +exclog.extra_info = true + +rattail.config = %(__file__)s + +[server:main] +use = egg:waitress#main +# to bind to all interfaces, set host to 0.0.0.0 +host = 127.0.0.1 +port = 9080 + + +############################## +# logging +############################## + +[handler_console] +level = INFO + +[handler_file] +args = (r'applogweb.log', 'a', 1000000, 100, 'utf_8')