-
-
Save mmerickel/6e484657f2244fc6876e6bfb8ed97bc3 to your computer and use it in GitHub Desktop.
render an ini file based on env vars
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
############################################################################## | |
# Settings made available in site.ini.in. | |
# | |
# After making changes re-run render-config | |
# | |
# Override settings by making a .env.local file. | |
############################################################################## | |
DEBUG=yes | |
WEB_CONCURRENCY=10 | |
TRUSTED_PROXY="" | |
TRUSTED_PROXY_HEADERS="" | |
TRUSTED_PROXY_COUNT=1 | |
LOG_UNTRUSTED_PROXY_HEADERS=yes | |
BIND_HOST=127.0.0.1 | |
# heroku defines the PORT variable but we rename it to BIND_PORT | |
PORT=10001 | |
BIND_PORT=${PORT} | |
DATABASE_URL=... | |
RETRY_ATTEMPTS=3 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from dotenv import load_dotenv | |
import jinja2 | |
from jinja2.ext import Extension | |
from jinja2.lexer import Token | |
from markupsafe import Markup | |
import os | |
from pyramid.settings import asbool | |
import textwrap | |
def asbool_filter(value): | |
return asbool(value) | |
def multiline_filter(value): | |
# strip only the leading whitespaces from the first line | |
value = value.strip(' ').strip('\n') | |
if value: | |
lines = textwrap.dedent(value).split('\n') | |
return '\n ' + '\n '.join(lines) | |
return '' | |
def ini_filter(value): | |
if isinstance(value, Markup): | |
return value | |
if not isinstance(value, str): | |
return value | |
return value.replace('%', '%%') | |
class IniEverythingExtension(Extension): | |
""" | |
Insert a `|ini` filter at the end of every variable substitution. | |
This will ensure that all injected values are converted to INI. | |
""" | |
def filter_stream(self, stream): | |
# This is based on | |
# https://github.com/indico/indico/blob/master/indico/web/flask/templating.py. | |
for token in stream: | |
if token.type == 'variable_end': | |
yield Token(token.lineno, 'pipe', '|') | |
yield Token(token.lineno, 'name', 'ini') | |
yield token | |
def render_template(path, context): | |
base, name = os.path.split(path) | |
if not base: | |
base = os.getcwd() | |
env = jinja2.Environment( | |
loader=jinja2.FileSystemLoader([base]), | |
undefined=jinja2.StrictUndefined, | |
extensions=[IniEverythingExtension], | |
) | |
env.filters['bool'] = asbool_filter | |
env.filters['multiline'] = multiline_filter | |
env.filters['ini'] = ini_filter | |
template = env.get_template(name) | |
return template.render(context) | |
def parse_args(): | |
parser = argparse.ArgumentParser() | |
parser.add_argument('--env') | |
parser.add_argument('--env-file') | |
parser.add_argument('--template', default='site.ini.in') | |
parser.add_argument('-o', '--output-file', default='site.ini') | |
def main(cli, args): | |
if args.env_file: | |
load_dotenv(args.env_file) | |
else: | |
override = args.env or os.environ.get('DOTENV') | |
for path in ( | |
f'.env.{override}.local' if override else None, | |
f'.env.{override}' if override else None, | |
'.env.local', | |
'.env', | |
): | |
if path and os.path.exists(path): | |
load_dotenv(path) | |
result = render_template(args.template, { | |
'env': os.environ, | |
}) | |
with open(args.output_file, 'w', encoding='utf8') as fp: | |
fp.write(result.rstrip() + '\n') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{%- set DEBUG = env.DEBUG | bool -%} | |
[alembic] | |
script_location = myapp.alembic:migrations | |
file_template = %%(year)d%%(month).2d%%(day).2d_%%(rev)s | |
sqlalchemy.url = {{ env.DATABASE_URL }} | |
[app:api] | |
use = egg:myapp | |
sqlalchemy.url = {{ env.DATABASE_URL }} | |
retry.attempts = {{ env.RETRY_ATTEMPTS }} | |
tm.annotate_user = no | |
pyramid.reload_assets = {{ 'yes' if DEBUG else 'no' }} | |
pyramid.reload_templates = {{ 'yes' if DEBUG else 'no' }} | |
pyramid.debug_authorization = no | |
pyramid.debug_notfound = no | |
pyramid.debug_routematch = no | |
pyramid.default_locale_name = en | |
{%- if DEBUG %} | |
pyramid.includes = | |
pyramid_debugtoolbar | |
debugtoolbar.hosts = 0.0.0.0/0 | |
debugtoolbar.show_on_exc_only = yes | |
debugtoolbar.active_panels = performance | |
{%- endif %} | |
[filter:tracker] | |
use = egg:request-id | |
exclude_prefixes = | |
/static/ | |
/_debug_toolbar/ | |
[pipeline:main] | |
pipeline = | |
tracker | |
api | |
[server:main] | |
use = egg:waitress#main | |
host = {{ env.BIND_HOST }} | |
port = {{ env.BIND_PORT }} | |
threads = {{ env.WEB_CONCURRENCY }} | |
{%- if env.TRUSTED_PROXY %} | |
trusted_proxy = {{ env.TRUSTED_PROXY }} | |
trusted_proxy_headers = {{ env.TRUSTED_PROXY_HEADERS }} | |
trusted_proxy_count = {{ env.TRUSTED_PROXY_COUNT }} | |
{%- endif %} | |
log_untrusted_proxy_headers = {{ env.LOG_UNTRUSTED_PROXY_HEADERS }} | |
clear_untrusted_proxy_headers = yes | |
[loggers] | |
keys = root, alembic, sqlalchemy, myapp, translogger | |
[handlers] | |
keys = console, translogger | |
[formatters] | |
keys = generic, minimal | |
[logger_root] | |
level = INFO | |
handlers = console | |
[logger_alembic] | |
level = INFO | |
handlers = | |
qualname = alembic | |
[logger_sqlalchemy] | |
level = WARN | |
handlers = | |
qualname = sqlalchemy | |
[logger_myapp] | |
level = DEBUG | |
handlers = | |
qualname = myapp | |
[logger_translogger] | |
level = INFO | |
handlers = translogger | |
qualname = request_id | |
propagate = 0 | |
[handler_console] | |
class = StreamHandler | |
args = (sys.stderr,) | |
level = NOTSET | |
formatter = generic | |
[handler_translogger] | |
class = StreamHandler | |
args = (sys.stderr,) | |
level = NOTSET | |
formatter = minimal | |
[formatter_generic] | |
class = Formatter | |
format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s | |
[formatter_minimal] | |
class = Formatter | |
format = %(message)s |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
what is {{ foo }}? Does it count foo as a variable?