2017-10-27 07:08:24 +00:00
|
|
|
import click
|
2018-05-20 17:01:49 +00:00
|
|
|
from click import formatting
|
2017-11-04 23:53:50 +00:00
|
|
|
from click_default_group import DefaultGroup
|
2017-11-11 20:10:51 +00:00
|
|
|
import json
|
2017-12-03 16:33:36 +00:00
|
|
|
import os
|
2017-11-11 16:00:00 +00:00
|
|
|
import shutil
|
2017-11-15 19:53:00 +00:00
|
|
|
from subprocess import call, check_output
|
2017-11-11 16:00:00 +00:00
|
|
|
import sys
|
2018-05-20 17:01:49 +00:00
|
|
|
from .app import Datasette, DEFAULT_CONFIG, CONFIG_OPTIONS
|
2018-06-16 16:44:31 +00:00
|
|
|
from .utils import (
|
|
|
|
temporary_docker_directory,
|
|
|
|
temporary_heroku_directory,
|
|
|
|
value_as_boolean,
|
|
|
|
ValueAsBooleanError,
|
|
|
|
)
|
2017-10-27 07:08:24 +00:00
|
|
|
|
2017-11-04 23:53:50 +00:00
|
|
|
|
2017-12-09 18:19:39 +00:00
|
|
|
class StaticMount(click.ParamType):
|
2018-04-18 14:14:21 +00:00
|
|
|
name = "static mount"
|
2017-12-09 18:19:39 +00:00
|
|
|
|
|
|
|
def convert(self, value, param, ctx):
|
2018-04-18 14:14:21 +00:00
|
|
|
if ":" not in value:
|
|
|
|
self.fail(
|
2018-05-18 05:08:26 +00:00
|
|
|
'"{}" should be of format mountpoint:directory'.format(value),
|
|
|
|
param, ctx
|
2018-04-18 14:14:21 +00:00
|
|
|
)
|
|
|
|
path, dirpath = value.split(":")
|
2017-12-09 18:19:39 +00:00
|
|
|
if not os.path.exists(dirpath) or not os.path.isdir(dirpath):
|
2018-04-18 14:14:21 +00:00
|
|
|
self.fail("%s is not a valid directory path" % value, param, ctx)
|
2017-12-09 18:19:39 +00:00
|
|
|
return path, dirpath
|
|
|
|
|
|
|
|
|
2018-05-20 17:01:49 +00:00
|
|
|
class Config(click.ParamType):
|
|
|
|
name = "config"
|
2018-05-18 05:08:26 +00:00
|
|
|
|
2018-05-25 01:12:27 +00:00
|
|
|
def convert(self, config, param, ctx):
|
|
|
|
if ":" not in config:
|
2018-05-18 05:08:26 +00:00
|
|
|
self.fail(
|
2018-05-25 01:12:27 +00:00
|
|
|
'"{}" should be name:value'.format(config), param, ctx
|
2018-05-18 05:08:26 +00:00
|
|
|
)
|
2018-05-25 01:12:27 +00:00
|
|
|
return
|
|
|
|
name, value = config.split(":")
|
2018-05-20 17:01:49 +00:00
|
|
|
if name not in DEFAULT_CONFIG:
|
2018-06-16 16:44:31 +00:00
|
|
|
self.fail(
|
|
|
|
"{} is not a valid option (--help-config to see all)".format(
|
|
|
|
name
|
|
|
|
), param, ctx
|
|
|
|
)
|
2018-05-25 01:12:27 +00:00
|
|
|
return
|
|
|
|
# Type checking
|
|
|
|
default = DEFAULT_CONFIG[name]
|
|
|
|
if isinstance(default, bool):
|
2018-06-16 16:44:31 +00:00
|
|
|
try:
|
|
|
|
return name, value_as_boolean(value)
|
|
|
|
except ValueAsBooleanError:
|
2018-05-25 01:12:27 +00:00
|
|
|
self.fail(
|
2018-06-16 16:44:31 +00:00
|
|
|
'"{}" should be on/off/true/false/1/0'.format(name), param, ctx
|
2018-05-25 01:12:27 +00:00
|
|
|
)
|
|
|
|
return
|
|
|
|
elif isinstance(default, int):
|
|
|
|
if not value.isdigit():
|
|
|
|
self.fail(
|
|
|
|
'"{}" should be an integer'.format(name), param, ctx
|
|
|
|
)
|
|
|
|
return
|
|
|
|
return name, int(value)
|
|
|
|
else:
|
|
|
|
# Should never happen:
|
|
|
|
self.fail('Invalid option')
|
2018-05-18 05:08:26 +00:00
|
|
|
|
|
|
|
|
2018-04-18 14:14:21 +00:00
|
|
|
@click.group(cls=DefaultGroup, default="serve", default_if_no_args=True)
|
2017-11-19 05:59:16 +00:00
|
|
|
@click.version_option()
|
2017-10-27 07:08:24 +00:00
|
|
|
def cli():
|
|
|
|
"""
|
2017-11-10 18:38:35 +00:00
|
|
|
Datasette!
|
2017-10-27 07:08:24 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
@cli.command()
|
2018-04-18 14:14:21 +00:00
|
|
|
@click.argument("files", type=click.Path(exists=True), nargs=-1)
|
|
|
|
@click.option("--inspect-file", default="inspect-data.json")
|
2017-11-26 23:01:53 +00:00
|
|
|
@click.option(
|
2018-04-18 14:14:21 +00:00
|
|
|
"sqlite_extensions",
|
|
|
|
"--load-extension",
|
|
|
|
envvar="SQLITE_EXTENSIONS",
|
|
|
|
multiple=True,
|
|
|
|
type=click.Path(exists=True, resolve_path=True),
|
|
|
|
help="Path to a SQLite extension to load",
|
2017-11-26 23:01:53 +00:00
|
|
|
)
|
2017-12-07 16:57:31 +00:00
|
|
|
def inspect(files, inspect_file, sqlite_extensions):
|
2017-11-26 23:01:53 +00:00
|
|
|
app = Datasette(files, sqlite_extensions=sqlite_extensions)
|
2018-04-18 14:14:21 +00:00
|
|
|
open(inspect_file, "w").write(json.dumps(app.inspect(), indent=2))
|
2017-10-27 07:08:24 +00:00
|
|
|
|
|
|
|
|
2017-11-11 07:25:22 +00:00
|
|
|
@cli.command()
|
2018-04-18 14:14:21 +00:00
|
|
|
@click.argument("publisher", type=click.Choice(["now", "heroku"]))
|
|
|
|
@click.argument("files", type=click.Path(exists=True), nargs=-1)
|
|
|
|
@click.option(
|
|
|
|
"-n",
|
|
|
|
"--name",
|
|
|
|
default="datasette",
|
2018-07-14 13:10:49 +00:00
|
|
|
help="Application name to use when deploying",
|
2018-04-18 14:14:21 +00:00
|
|
|
)
|
|
|
|
@click.option(
|
|
|
|
"-m",
|
|
|
|
"--metadata",
|
|
|
|
type=click.File(mode="r"),
|
|
|
|
help="Path to JSON file containing metadata to publish",
|
|
|
|
)
|
|
|
|
@click.option("--extra-options", help="Extra options to pass to datasette serve")
|
|
|
|
@click.option("--force", is_flag=True, help="Pass --force option to now")
|
|
|
|
@click.option("--branch", help="Install datasette from a GitHub branch e.g. master")
|
2018-06-17 19:46:52 +00:00
|
|
|
@click.option("--token", help="Auth token to use for deploy (Now only)")
|
2017-11-13 15:20:02 +00:00
|
|
|
@click.option(
|
2018-04-18 14:14:21 +00:00
|
|
|
"--template-dir",
|
|
|
|
type=click.Path(exists=True, file_okay=False, dir_okay=True),
|
|
|
|
help="Path to directory containing custom templates",
|
2017-11-13 15:20:02 +00:00
|
|
|
)
|
|
|
|
@click.option(
|
2018-04-18 14:14:21 +00:00
|
|
|
"--plugins-dir",
|
|
|
|
type=click.Path(exists=True, file_okay=False, dir_okay=True),
|
|
|
|
help="Path to directory containing custom plugins",
|
2017-11-13 15:20:02 +00:00
|
|
|
)
|
2018-04-18 14:14:21 +00:00
|
|
|
@click.option(
|
|
|
|
"--static",
|
|
|
|
type=StaticMount(),
|
|
|
|
help="mountpoint:path-to-directory for serving static files",
|
|
|
|
multiple=True,
|
|
|
|
)
|
2018-04-18 14:48:34 +00:00
|
|
|
@click.option(
|
|
|
|
"--install",
|
|
|
|
help="Additional packages (e.g. plugins) to install",
|
|
|
|
multiple=True,
|
|
|
|
)
|
2018-05-31 14:16:50 +00:00
|
|
|
@click.option(
|
|
|
|
"--spatialite", is_flag=True, help="Enable SpatialLite extension"
|
|
|
|
)
|
2018-06-17 20:14:55 +00:00
|
|
|
@click.option("--version-note", help="Additional note to show on /-/versions")
|
2018-04-18 14:14:21 +00:00
|
|
|
@click.option("--title", help="Title for metadata")
|
|
|
|
@click.option("--license", help="License label for metadata")
|
|
|
|
@click.option("--license_url", help="License URL for metadata")
|
|
|
|
@click.option("--source", help="Source label for metadata")
|
|
|
|
@click.option("--source_url", help="Source URL for metadata")
|
|
|
|
def publish(
|
|
|
|
publisher,
|
|
|
|
files,
|
|
|
|
name,
|
|
|
|
metadata,
|
|
|
|
extra_options,
|
|
|
|
force,
|
|
|
|
branch,
|
2018-06-17 19:46:52 +00:00
|
|
|
token,
|
2018-04-18 14:14:21 +00:00
|
|
|
template_dir,
|
|
|
|
plugins_dir,
|
|
|
|
static,
|
2018-04-18 14:48:34 +00:00
|
|
|
install,
|
2018-05-31 14:16:50 +00:00
|
|
|
spatialite,
|
2018-06-17 20:14:55 +00:00
|
|
|
version_note,
|
2018-04-18 14:14:21 +00:00
|
|
|
**extra_metadata
|
|
|
|
):
|
2017-11-13 18:40:51 +00:00
|
|
|
"""
|
|
|
|
Publish specified SQLite database files to the internet along with a datasette API.
|
|
|
|
|
2017-11-21 18:10:48 +00:00
|
|
|
Options for PUBLISHER:
|
|
|
|
* 'now' - You must have Zeit Now installed: https://zeit.co/now
|
|
|
|
* 'heroku' - You must have Heroku installed: https://cli.heroku.com/
|
2017-11-13 18:40:51 +00:00
|
|
|
|
|
|
|
Example usage: datasette publish now my-database.db
|
|
|
|
"""
|
2018-04-18 14:14:21 +00:00
|
|
|
|
2017-11-21 18:10:48 +00:00
|
|
|
def _fail_if_publish_binary_not_installed(binary, publish_target, install_link):
|
|
|
|
"""Exit (with error message) if ``binary` isn't installed"""
|
|
|
|
if not shutil.which(binary):
|
2017-11-15 19:53:00 +00:00
|
|
|
click.secho(
|
2017-11-22 17:42:29 +00:00
|
|
|
"Publishing to {publish_target} requires {binary} to be installed and configured".format(
|
2018-04-18 14:14:21 +00:00
|
|
|
publish_target=publish_target, binary=binary
|
2017-11-22 17:42:29 +00:00
|
|
|
),
|
2018-04-18 14:14:21 +00:00
|
|
|
bg="red",
|
|
|
|
fg="white",
|
2017-11-15 19:53:00 +00:00
|
|
|
bold=True,
|
2018-04-18 14:14:21 +00:00
|
|
|
err=True,
|
|
|
|
)
|
|
|
|
click.echo(
|
|
|
|
"Follow the instructions at {install_link}".format(
|
|
|
|
install_link=install_link
|
|
|
|
),
|
|
|
|
err=True,
|
2017-11-15 19:53:00 +00:00
|
|
|
)
|
|
|
|
sys.exit(1)
|
2017-11-13 16:13:38 +00:00
|
|
|
|
2018-04-18 14:14:21 +00:00
|
|
|
if publisher == "now":
|
|
|
|
_fail_if_publish_binary_not_installed("now", "Zeit Now", "https://zeit.co/now")
|
2018-07-24 01:51:43 +00:00
|
|
|
if extra_options:
|
|
|
|
extra_options += " "
|
|
|
|
else:
|
|
|
|
extra_options = ""
|
|
|
|
extra_options += "--config force_https_urls:on"
|
|
|
|
|
2018-04-18 14:14:21 +00:00
|
|
|
with temporary_docker_directory(
|
|
|
|
files,
|
|
|
|
name,
|
|
|
|
metadata,
|
|
|
|
extra_options,
|
|
|
|
branch,
|
|
|
|
template_dir,
|
|
|
|
plugins_dir,
|
|
|
|
static,
|
2018-04-18 14:48:34 +00:00
|
|
|
install,
|
2018-05-31 14:16:50 +00:00
|
|
|
spatialite,
|
2018-06-17 20:14:55 +00:00
|
|
|
version_note,
|
2018-04-18 14:14:21 +00:00
|
|
|
extra_metadata,
|
|
|
|
):
|
2018-06-17 19:46:52 +00:00
|
|
|
args = []
|
2017-11-15 19:53:00 +00:00
|
|
|
if force:
|
2018-06-17 19:46:52 +00:00
|
|
|
args.append("--force")
|
|
|
|
if token:
|
|
|
|
args.append("--token={}".format(token))
|
|
|
|
if args:
|
|
|
|
call(["now"] + args)
|
2017-11-15 19:53:00 +00:00
|
|
|
else:
|
2018-04-18 14:14:21 +00:00
|
|
|
call("now")
|
2017-11-15 19:53:00 +00:00
|
|
|
|
2018-04-18 14:14:21 +00:00
|
|
|
elif publisher == "heroku":
|
|
|
|
_fail_if_publish_binary_not_installed(
|
|
|
|
"heroku", "Heroku", "https://cli.heroku.com"
|
|
|
|
)
|
2018-05-31 14:21:27 +00:00
|
|
|
if spatialite:
|
|
|
|
click.secho(
|
|
|
|
"The --spatialite option is not yet supported for Heroku",
|
|
|
|
bg="red",
|
|
|
|
fg="white",
|
|
|
|
bold=True,
|
|
|
|
err=True,
|
|
|
|
)
|
|
|
|
click.echo(
|
|
|
|
"See https://github.com/simonw/datasette/issues/301",
|
|
|
|
err=True,
|
|
|
|
)
|
|
|
|
sys.exit(1)
|
2017-11-21 18:10:48 +00:00
|
|
|
|
|
|
|
# Check for heroku-builds plugin
|
2018-04-18 14:14:21 +00:00
|
|
|
plugins = [
|
|
|
|
line.split()[0] for line in check_output(["heroku", "plugins"]).splitlines()
|
|
|
|
]
|
|
|
|
if b"heroku-builds" not in plugins:
|
|
|
|
click.echo(
|
|
|
|
"Publishing to Heroku requires the heroku-builds plugin to be installed."
|
|
|
|
)
|
|
|
|
click.confirm(
|
|
|
|
"Install it? (this will run `heroku plugins:install heroku-builds`)",
|
|
|
|
abort=True,
|
|
|
|
)
|
2017-11-21 18:10:48 +00:00
|
|
|
call(["heroku", "plugins:install", "heroku-builds"])
|
|
|
|
|
2018-04-18 14:14:21 +00:00
|
|
|
with temporary_heroku_directory(
|
|
|
|
files,
|
|
|
|
name,
|
|
|
|
metadata,
|
|
|
|
extra_options,
|
|
|
|
branch,
|
|
|
|
template_dir,
|
|
|
|
plugins_dir,
|
|
|
|
static,
|
2018-04-18 14:48:34 +00:00
|
|
|
install,
|
2018-04-18 14:14:21 +00:00
|
|
|
extra_metadata,
|
|
|
|
):
|
2018-07-14 13:10:49 +00:00
|
|
|
|
|
|
|
app_name = None
|
|
|
|
if name:
|
|
|
|
# Check to see if this app already exists
|
|
|
|
list_output = check_output(["heroku", "apps:list", "--json"]).decode('utf8')
|
|
|
|
apps = json.loads(list_output)
|
|
|
|
|
|
|
|
for app in apps:
|
|
|
|
if app['name'] == name:
|
|
|
|
app_name = name
|
|
|
|
break
|
|
|
|
|
|
|
|
if not app_name:
|
|
|
|
# Create a new app
|
|
|
|
cmd = ["heroku", "apps:create"]
|
|
|
|
if name:
|
|
|
|
cmd.append(name)
|
|
|
|
cmd.append("--json")
|
|
|
|
create_output = check_output(cmd).decode(
|
|
|
|
"utf8"
|
|
|
|
)
|
|
|
|
app_name = json.loads(create_output)["name"]
|
|
|
|
|
2017-11-15 19:53:00 +00:00
|
|
|
call(["heroku", "builds:create", "-a", app_name])
|
2017-11-13 16:13:38 +00:00
|
|
|
|
2017-11-22 17:42:29 +00:00
|
|
|
|
2017-12-07 06:20:37 +00:00
|
|
|
@cli.command()
|
2018-04-18 14:14:21 +00:00
|
|
|
@click.argument("files", type=click.Path(exists=True), nargs=-1, required=True)
|
2017-12-07 06:20:37 +00:00
|
|
|
@click.option(
|
2018-04-18 14:14:21 +00:00
|
|
|
"-m",
|
|
|
|
"--metadata",
|
|
|
|
default="metadata.json",
|
|
|
|
help="Name of metadata file to generate",
|
2017-12-07 06:20:37 +00:00
|
|
|
)
|
|
|
|
@click.option(
|
2018-04-18 14:14:21 +00:00
|
|
|
"sqlite_extensions",
|
|
|
|
"--load-extension",
|
|
|
|
envvar="SQLITE_EXTENSIONS",
|
|
|
|
multiple=True,
|
|
|
|
type=click.Path(exists=True, resolve_path=True),
|
|
|
|
help="Path to a SQLite extension to load",
|
2017-12-07 06:20:37 +00:00
|
|
|
)
|
|
|
|
def skeleton(files, metadata, sqlite_extensions):
|
|
|
|
"Generate a skeleton metadata.json file for specified SQLite databases"
|
|
|
|
if os.path.exists(metadata):
|
|
|
|
click.secho(
|
2018-04-18 14:14:21 +00:00
|
|
|
"File {} already exists, will not over-write".format(metadata),
|
|
|
|
bg="red",
|
|
|
|
fg="white",
|
2017-12-07 06:20:37 +00:00
|
|
|
bold=True,
|
|
|
|
err=True,
|
|
|
|
)
|
|
|
|
sys.exit(1)
|
|
|
|
app = Datasette(files, sqlite_extensions=sqlite_extensions)
|
|
|
|
databases = {}
|
|
|
|
for database_name, info in app.inspect().items():
|
|
|
|
databases[database_name] = {
|
2018-04-18 14:14:21 +00:00
|
|
|
"title": None,
|
|
|
|
"description": None,
|
|
|
|
"description_html": None,
|
|
|
|
"license": None,
|
|
|
|
"license_url": None,
|
|
|
|
"source": None,
|
|
|
|
"source_url": None,
|
|
|
|
"queries": {},
|
|
|
|
"tables": {
|
2017-12-07 06:20:37 +00:00
|
|
|
table_name: {
|
2018-04-18 14:14:21 +00:00
|
|
|
"title": None,
|
|
|
|
"description": None,
|
|
|
|
"description_html": None,
|
|
|
|
"license": None,
|
|
|
|
"license_url": None,
|
|
|
|
"source": None,
|
|
|
|
"source_url": None,
|
|
|
|
"units": {},
|
|
|
|
}
|
|
|
|
for table_name in (info.get("tables") or {})
|
|
|
|
},
|
2017-12-07 06:20:37 +00:00
|
|
|
}
|
2018-04-18 14:14:21 +00:00
|
|
|
open(metadata, "w").write(
|
|
|
|
json.dumps(
|
|
|
|
{
|
|
|
|
"title": None,
|
|
|
|
"description": None,
|
|
|
|
"description_html": None,
|
|
|
|
"license": None,
|
|
|
|
"license_url": None,
|
|
|
|
"source": None,
|
|
|
|
"source_url": None,
|
|
|
|
"databases": databases,
|
|
|
|
},
|
|
|
|
indent=4,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
click.echo("Wrote skeleton to {}".format(metadata))
|
2017-12-07 06:20:37 +00:00
|
|
|
|
|
|
|
|
2017-11-13 16:13:38 +00:00
|
|
|
@cli.command()
|
2018-04-18 14:14:21 +00:00
|
|
|
@click.argument("files", type=click.Path(exists=True), nargs=-1, required=True)
|
|
|
|
@click.option(
|
|
|
|
"-t",
|
|
|
|
"--tag",
|
|
|
|
help="Name for the resulting Docker container, can optionally use name:tag format",
|
|
|
|
)
|
|
|
|
@click.option(
|
|
|
|
"-m",
|
|
|
|
"--metadata",
|
|
|
|
type=click.File(mode="r"),
|
|
|
|
help="Path to JSON file containing metadata to publish",
|
|
|
|
)
|
|
|
|
@click.option("--extra-options", help="Extra options to pass to datasette serve")
|
|
|
|
@click.option("--branch", help="Install datasette from a GitHub branch e.g. master")
|
2017-11-13 16:13:38 +00:00
|
|
|
@click.option(
|
2018-04-18 14:14:21 +00:00
|
|
|
"--template-dir",
|
|
|
|
type=click.Path(exists=True, file_okay=False, dir_okay=True),
|
|
|
|
help="Path to directory containing custom templates",
|
2017-11-13 16:13:38 +00:00
|
|
|
)
|
|
|
|
@click.option(
|
2018-04-18 14:14:21 +00:00
|
|
|
"--plugins-dir",
|
|
|
|
type=click.Path(exists=True, file_okay=False, dir_okay=True),
|
|
|
|
help="Path to directory containing custom plugins",
|
2017-11-13 16:13:38 +00:00
|
|
|
)
|
2018-04-18 14:14:21 +00:00
|
|
|
@click.option(
|
|
|
|
"--static",
|
|
|
|
type=StaticMount(),
|
|
|
|
help="mountpoint:path-to-directory for serving static files",
|
|
|
|
multiple=True,
|
|
|
|
)
|
2018-04-18 14:48:34 +00:00
|
|
|
@click.option(
|
|
|
|
"--install",
|
|
|
|
help="Additional packages (e.g. plugins) to install",
|
|
|
|
multiple=True,
|
|
|
|
)
|
2018-05-31 14:16:50 +00:00
|
|
|
@click.option(
|
|
|
|
"--spatialite", is_flag=True, help="Enable SpatialLite extension"
|
|
|
|
)
|
2018-06-17 20:14:55 +00:00
|
|
|
@click.option("--version-note", help="Additional note to show on /-/versions")
|
2018-04-18 14:14:21 +00:00
|
|
|
@click.option("--title", help="Title for metadata")
|
|
|
|
@click.option("--license", help="License label for metadata")
|
|
|
|
@click.option("--license_url", help="License URL for metadata")
|
|
|
|
@click.option("--source", help="Source label for metadata")
|
|
|
|
@click.option("--source_url", help="Source URL for metadata")
|
|
|
|
def package(
|
|
|
|
files,
|
|
|
|
tag,
|
|
|
|
metadata,
|
|
|
|
extra_options,
|
|
|
|
branch,
|
|
|
|
template_dir,
|
|
|
|
plugins_dir,
|
|
|
|
static,
|
2018-04-18 14:48:34 +00:00
|
|
|
install,
|
2018-05-31 14:16:50 +00:00
|
|
|
spatialite,
|
2018-06-17 20:14:55 +00:00
|
|
|
version_note,
|
2018-04-18 14:14:21 +00:00
|
|
|
**extra_metadata
|
|
|
|
):
|
2017-11-13 16:13:38 +00:00
|
|
|
"Package specified SQLite files into a new datasette Docker container"
|
2018-04-18 14:14:21 +00:00
|
|
|
if not shutil.which("docker"):
|
2017-11-13 16:13:38 +00:00
|
|
|
click.secho(
|
|
|
|
' The package command requires "docker" to be installed and configured ',
|
2018-04-18 14:14:21 +00:00
|
|
|
bg="red",
|
|
|
|
fg="white",
|
2017-11-13 16:13:38 +00:00
|
|
|
bold=True,
|
|
|
|
err=True,
|
|
|
|
)
|
|
|
|
sys.exit(1)
|
2018-04-18 14:14:21 +00:00
|
|
|
with temporary_docker_directory(
|
|
|
|
files,
|
|
|
|
"datasette",
|
|
|
|
metadata,
|
|
|
|
extra_options,
|
|
|
|
branch,
|
|
|
|
template_dir,
|
|
|
|
plugins_dir,
|
|
|
|
static,
|
2018-04-18 14:48:34 +00:00
|
|
|
install,
|
2018-05-31 14:16:50 +00:00
|
|
|
spatialite,
|
2018-06-17 20:14:55 +00:00
|
|
|
version_note,
|
2018-04-18 14:14:21 +00:00
|
|
|
extra_metadata,
|
|
|
|
):
|
|
|
|
args = ["docker", "build"]
|
2017-11-13 16:13:38 +00:00
|
|
|
if tag:
|
2018-04-18 14:14:21 +00:00
|
|
|
args.append("-t")
|
2017-11-13 16:13:38 +00:00
|
|
|
args.append(tag)
|
2018-04-18 14:14:21 +00:00
|
|
|
args.append(".")
|
2017-11-13 16:13:38 +00:00
|
|
|
call(args)
|
2017-11-11 07:25:22 +00:00
|
|
|
|
|
|
|
|
2017-10-27 07:08:24 +00:00
|
|
|
@cli.command()
|
2018-04-18 14:14:21 +00:00
|
|
|
@click.argument("files", type=click.Path(exists=True), nargs=-1)
|
2017-11-17 14:13:35 +00:00
|
|
|
@click.option(
|
2018-04-18 14:14:21 +00:00
|
|
|
"-h", "--host", default="127.0.0.1", help="host for server, defaults to 127.0.0.1"
|
2017-11-17 14:13:35 +00:00
|
|
|
)
|
2018-04-18 14:14:21 +00:00
|
|
|
@click.option("-p", "--port", default=8001, help="port for server, defaults to 8001")
|
|
|
|
@click.option(
|
|
|
|
"--debug", is_flag=True, help="Enable debug mode - useful for development"
|
|
|
|
)
|
|
|
|
@click.option(
|
|
|
|
"--reload",
|
|
|
|
is_flag=True,
|
|
|
|
help="Automatically reload if code change detected - useful for development",
|
|
|
|
)
|
|
|
|
@click.option(
|
|
|
|
"--cors", is_flag=True, help="Enable CORS by serving Access-Control-Allow-Origin: *"
|
|
|
|
)
|
|
|
|
@click.option(
|
|
|
|
"sqlite_extensions",
|
|
|
|
"--load-extension",
|
|
|
|
envvar="SQLITE_EXTENSIONS",
|
|
|
|
multiple=True,
|
|
|
|
type=click.Path(exists=True, resolve_path=True),
|
|
|
|
help="Path to a SQLite extension to load",
|
|
|
|
)
|
|
|
|
@click.option(
|
|
|
|
"--inspect-file", help='Path to JSON file created using "datasette inspect"'
|
|
|
|
)
|
|
|
|
@click.option(
|
|
|
|
"-m",
|
|
|
|
"--metadata",
|
|
|
|
type=click.File(mode="r"),
|
|
|
|
help="Path to JSON file containing license/source metadata",
|
|
|
|
)
|
|
|
|
@click.option(
|
|
|
|
"--template-dir",
|
|
|
|
type=click.Path(exists=True, file_okay=False, dir_okay=True),
|
|
|
|
help="Path to directory containing custom templates",
|
|
|
|
)
|
|
|
|
@click.option(
|
|
|
|
"--plugins-dir",
|
|
|
|
type=click.Path(exists=True, file_okay=False, dir_okay=True),
|
|
|
|
help="Path to directory containing custom plugins",
|
|
|
|
)
|
|
|
|
@click.option(
|
|
|
|
"--static",
|
|
|
|
type=StaticMount(),
|
|
|
|
help="mountpoint:path-to-directory for serving static files",
|
|
|
|
multiple=True,
|
|
|
|
)
|
2018-05-18 05:08:26 +00:00
|
|
|
@click.option(
|
2018-05-20 17:01:49 +00:00
|
|
|
"--config",
|
|
|
|
type=Config(),
|
|
|
|
help="Set config option using configname:value datasette.readthedocs.io/en/latest/config.html",
|
2018-05-18 05:08:26 +00:00
|
|
|
multiple=True,
|
|
|
|
)
|
2018-06-17 20:14:55 +00:00
|
|
|
@click.option("--version-note", help="Additional note to show on /-/versions")
|
2018-05-20 17:01:49 +00:00
|
|
|
@click.option(
|
|
|
|
"--help-config",
|
|
|
|
is_flag=True,
|
|
|
|
help="Show available config options",
|
|
|
|
)
|
2018-04-18 14:14:21 +00:00
|
|
|
def serve(
|
|
|
|
files,
|
|
|
|
host,
|
|
|
|
port,
|
|
|
|
debug,
|
|
|
|
reload,
|
|
|
|
cors,
|
|
|
|
sqlite_extensions,
|
|
|
|
inspect_file,
|
|
|
|
metadata,
|
|
|
|
template_dir,
|
|
|
|
plugins_dir,
|
|
|
|
static,
|
2018-05-20 17:01:49 +00:00
|
|
|
config,
|
2018-06-17 20:14:55 +00:00
|
|
|
version_note,
|
2018-05-20 17:01:49 +00:00
|
|
|
help_config,
|
2018-04-18 14:14:21 +00:00
|
|
|
):
|
2017-11-13 18:41:53 +00:00
|
|
|
"""Serve up specified SQLite database files with a web UI"""
|
2018-05-20 17:01:49 +00:00
|
|
|
if help_config:
|
|
|
|
formatter = formatting.HelpFormatter()
|
|
|
|
with formatter.section("Config options"):
|
|
|
|
formatter.write_dl([
|
|
|
|
(option.name, '{} (default={})'.format(
|
|
|
|
option.help, option.default
|
|
|
|
))
|
|
|
|
for option in CONFIG_OPTIONS
|
|
|
|
])
|
|
|
|
click.echo(formatter.getvalue())
|
|
|
|
sys.exit(0)
|
2017-11-09 13:46:16 +00:00
|
|
|
if reload:
|
|
|
|
import hupper
|
2018-04-18 14:14:21 +00:00
|
|
|
|
|
|
|
reloader = hupper.start_reloader("datasette.cli.serve")
|
2017-12-07 16:42:28 +00:00
|
|
|
if metadata:
|
|
|
|
reloader.watch_files([metadata.name])
|
2017-11-09 13:46:16 +00:00
|
|
|
|
2017-11-13 15:20:02 +00:00
|
|
|
inspect_data = None
|
|
|
|
if inspect_file:
|
|
|
|
inspect_data = json.load(open(inspect_file))
|
|
|
|
|
|
|
|
metadata_data = None
|
2017-11-11 20:10:51 +00:00
|
|
|
if metadata:
|
2017-11-13 15:20:02 +00:00
|
|
|
metadata_data = json.loads(metadata.read())
|
2017-11-11 20:10:51 +00:00
|
|
|
|
2018-04-18 14:14:21 +00:00
|
|
|
click.echo("Serve! files={} on port {}".format(files, port))
|
2017-11-13 18:03:52 +00:00
|
|
|
ds = Datasette(
|
2017-11-11 20:10:51 +00:00
|
|
|
files,
|
|
|
|
cache_headers=not debug and not reload,
|
2017-11-13 18:17:42 +00:00
|
|
|
cors=cors,
|
2017-11-13 15:20:02 +00:00
|
|
|
inspect_data=inspect_data,
|
|
|
|
metadata=metadata_data,
|
2017-11-17 14:13:35 +00:00
|
|
|
sqlite_extensions=sqlite_extensions,
|
2017-11-30 16:05:01 +00:00
|
|
|
template_dir=template_dir,
|
2018-04-16 05:22:01 +00:00
|
|
|
plugins_dir=plugins_dir,
|
2017-12-03 16:33:36 +00:00
|
|
|
static_mounts=static,
|
2018-05-20 17:01:49 +00:00
|
|
|
config=dict(config),
|
2018-06-17 20:14:55 +00:00
|
|
|
version_note=version_note,
|
2017-11-13 18:03:52 +00:00
|
|
|
)
|
|
|
|
# Force initial hashing/table counting
|
|
|
|
ds.inspect()
|
|
|
|
ds.app().run(host=host, port=port, debug=debug)
|