diff --git a/datasette/cli.py b/datasette/cli.py index d4f91bbe..ccac5a74 100644 --- a/datasette/cli.py +++ b/datasette/cli.py @@ -2,11 +2,11 @@ import click from click_default_group import DefaultGroup import json import shutil -from subprocess import call +from subprocess import call, check_output import sys from .app import Datasette from .utils import ( - temporary_docker_directory, + temporary_docker_directory, temporary_heroku_directory ) @@ -26,7 +26,7 @@ def build(files, inspect_file): @cli.command() -@click.argument('publisher', type=click.Choice(['now'])) +@click.argument('publisher', type=click.Choice(['now', 'heroku'])) @click.argument('files', type=click.Path(exists=True), nargs=-1) @click.option( '-n', '--name', default='datasette', @@ -52,22 +52,34 @@ def publish(publisher, files, name, metadata, extra_options, force, **extra_meta Example usage: datasette publish now my-database.db """ - if not shutil.which('now'): - click.secho( - ' The publish command requires "now" to be installed and configured ', - bg='red', - fg='white', - bold=True, - err=True, - ) - click.echo('Follow the instructions at https://zeit.co/now#whats-now', err=True) - sys.exit(1) + if publisher == 'now': + if not shutil.which('now'): + click.secho( + ' The publish command requires "now" to be installed and configured ', + bg='red', + fg='white', + bold=True, + err=True, + ) + click.echo('Follow the instructions at https://zeit.co/now#whats-now', err=True) + sys.exit(1) - with temporary_docker_directory(files, name, metadata, extra_options, extra_metadata): - if force: - call(['now', '--force']) - else: - call('now') + with temporary_docker_directory(files, name, metadata, extra_options, extra_metadata): + if force: + call(['now', '--force']) + else: + call('now') + + elif publisher == 'heroku': + # FIXME: need to verify we have heroku, heroku-builds, and are logged in (ugh) + with temporary_heroku_directory(files, name, metadata, extra_options, extra_metadata): + # build(files) doesn't work, dunno why + app = Datasette(files) + open("inspect-data.json", 'w').write(json.dumps(app.inspect(), indent=2)) + + create_output = check_output(['heroku', 'apps:create', '--json']) + app_name = json.loads(create_output)["name"] + call(["heroku", "builds:create", "-a", app_name]) @cli.command() diff --git a/datasette/utils.py b/datasette/utils.py index 21061cdf..fcccf0b8 100644 --- a/datasette/utils.py +++ b/datasette/utils.py @@ -7,6 +7,7 @@ import sqlite3 import tempfile import time import urllib +import shlex def compound_pks_from_path(path): @@ -197,3 +198,48 @@ def temporary_docker_directory(files, name, metadata, extra_options, extra_metad finally: tmp.cleanup() os.chdir(saved_cwd) + +@contextmanager +def temporary_heroku_directory(files, name, metadata, extra_options, extra_metadata=None): + # FIXME: lots of duplicated code from above + + extra_metadata = extra_metadata or {} + tmp = tempfile.TemporaryDirectory() + saved_cwd = os.getcwd() + + file_paths = [ + os.path.join(saved_cwd, name) + for name in files + ] + file_names = [os.path.split(f)[-1] for f in files] + + if metadata: + metadata_content = json.load(metadata) + else: + metadata_content = {} + for key, value in extra_metadata.items(): + if value: + metadata_content[key] = value + + try: + os.chdir(tmp.name) + + if metadata_content: + open('metadata.json', 'w').write(json.dumps(metadata_content, indent=2)) + + open('runtime.txt', 'w').write('python-3.6.2') + open('requirements.txt', 'w').write('datasette') + + quoted_files = " ".join(map(shlex.quote, files)) + procfile_cmd = f'web: datasette serve --host 0.0.0.0 {quoted_files} --cors --port $PORT --inspect-file inspect-data.json' + open('Procfile', 'w').write(procfile_cmd) + + for path, filename in zip(file_paths, file_names): + os.link(path, os.path.join(tmp.name, filename)) + + yield + + finally: + tmp.cleanup() + os.chdir(saved_cwd) +