diff --git a/deadsands/pyproject.toml b/deadsands/pyproject.toml index 70ca884..ab52828 100644 --- a/deadsands/pyproject.toml +++ b/deadsands/pyproject.toml @@ -15,7 +15,7 @@ python = "^3.10" typer = "latest" rich = "latest" pyyaml = "latest" -invoke = "latest" +livereload = "latest" # static website pelican = "^4.7.2" diff --git a/deadsands/site_tools/cli.py b/deadsands/site_tools/cli.py index a3248fe..889504f 100644 --- a/deadsands/site_tools/cli.py +++ b/deadsands/site_tools/cli.py @@ -1,14 +1,41 @@ import click +import os +import shutil +import subprocess import sys import typer +import webbrowser from enum import Enum -from site_tools.tasks import CONFIG +from livereload import Server from site_tools.content_manager import create -CONFIG.update( - templates_path='markdown-templates', -) +from pelican import main as pelican_main +from pelican.settings import DEFAULT_CONFIG, get_settings_from_file + +OPEN_BROWSER_ON_SERVE = True +SETTINGS_FILE_BASE = 'pelicanconf.py' +SETTINGS = {} +SETTINGS.update(DEFAULT_CONFIG) +LOCAL_SETTINGS = get_settings_from_file(SETTINGS_FILE_BASE) +SETTINGS.update(LOCAL_SETTINGS) + +CONFIG = { + 'settings_base': SETTINGS_FILE_BASE, + 'settings_publish': 'publishconf.py', + # Output path. Can be absolute or relative to tasks.py. Default: 'output' + 'deploy_path': SETTINGS['OUTPUT_PATH'], + # Remote server configuration + 'ssh_user': 'greg', + 'ssh_host': 'froghat.club', + 'ssh_port': '22', + 'ssh_path': '/usr/local/deploy/deadsands/', + # Host and port for `serve` + 'host': 'localhost', + 'port': 8000, + # content manager config + 'templates_path': 'markdown-templates', +} app = typer.Typer() @@ -21,6 +48,75 @@ class ContentType(str, Enum): page = 'page' +def pelican_run(cmd: list = [], publish=False) -> None: + settings = CONFIG['settings_publish' if publish else 'settings_base'] + pelican_main(['-s', settings] + cmd) + + +@app.command() +def clean() -> None: + if os.path.isdir(CONFIG['deploy_path']): + shutil.rmtree(CONFIG['deploy_path']) + os.makedirs(CONFIG['deploy_path']) + + +@app.command() +def build() -> None: + pelican_run() + + +@app.command() +def serve() -> None: + + url = 'http://{host}:{port}/'.format(**CONFIG) + + def cached_build(): + pelican_run(['-e', 'CACHE_CONTENT=true', 'LOAD_CONTENT_CACHE=true', + 'SHOW_DRAFTS=true', f'SITEURL="{url}"']) + + clean() + cached_build() + server = Server() + theme_path = SETTINGS['THEME'] + watched_globs = [ + CONFIG['settings_base'], + '{}/templates/**/*.html'.format(theme_path), + ] + + content_file_extensions = ['.md', '.rst'] + for extension in content_file_extensions: + content_glob = '{0}/**/*{1}'.format(SETTINGS['PATH'], extension) + watched_globs.append(content_glob) + + static_file_extensions = ['.css', '.js'] + for extension in static_file_extensions: + static_file_glob = '{0}/static/**/*{1}'.format(theme_path, extension) + watched_globs.append(static_file_glob) + + for glob in watched_globs: + server.watch(glob, cached_build) + + if OPEN_BROWSER_ON_SERVE: + webbrowser.open(url) + + server.serve(host=CONFIG['host'], port=CONFIG['port'], + root=CONFIG['deploy_path']) + + +@app.command() +def publish(): + clean() + pelican_run(publish=True) + subprocess.call( + 'rsync --delete --exclude ".DS_Store" -pthrvz -c ' + '-e "ssh -p {ssh_port}" ' + '{} {ssh_user}@{ssh_host}:{ssh_path}'.format( + CONFIG['deploy_path'].rstrip('/') + '/', + **CONFIG + ) + ) + + @app.command() def new( content_type: ContentType = typer.Argument( diff --git a/deadsands/site_tools/tasks.py b/deadsands/site_tools/tasks.py deleted file mode 100644 index 3b00a02..0000000 --- a/deadsands/site_tools/tasks.py +++ /dev/null @@ -1,133 +0,0 @@ -# -*- coding: utf-8 -*- - -import os -import shlex -import shutil - -from invoke import task -from invoke.main import program -from pelican import main as pelican_main -from pelican.settings import DEFAULT_CONFIG, get_settings_from_file - -OPEN_BROWSER_ON_SERVE = True -SETTINGS_FILE_BASE = 'pelicanconf.py' -SETTINGS = {} -SETTINGS.update(DEFAULT_CONFIG) -LOCAL_SETTINGS = get_settings_from_file(SETTINGS_FILE_BASE) -SETTINGS.update(LOCAL_SETTINGS) - -CONFIG = { - 'settings_base': SETTINGS_FILE_BASE, - 'settings_publish': 'publishconf.py', - # Output path. Can be absolute or relative to tasks.py. Default: 'output' - 'deploy_path': SETTINGS['OUTPUT_PATH'], - # Remote server configuration - 'ssh_user': 'greg', - 'ssh_host': 'froghat.club', - 'ssh_port': '22', - 'ssh_path': '/usr/local/deploy/deadsands/', - # Host and port for `serve` - 'host': 'localhost', - 'port': 8000, -} - - -@task -def clean(c): - """Remove generated files""" - if os.path.isdir(CONFIG['deploy_path']): - shutil.rmtree(CONFIG['deploy_path']) - os.makedirs(CONFIG['deploy_path']) - - -@task -def build(c): - """Build local version of site""" - pelican_run('-s {settings_base}'.format(**CONFIG)) - - -@task -def rebuild(c): - """`build` with the delete switch""" - pelican_run('-d -s {settings_base}'.format(**CONFIG)) - - -@task -def regenerate(c): - """Automatically regenerate site upon file modification""" - pelican_run('-r -s {settings_base}'.format(**CONFIG)) - - -@task -def serve(c): - """Serve site at http://$HOST:$PORT/ (default is localhost:8000)""" - pelican_run('-rl -b {host} -p {port} --relative-urls -D --ignore-cache ' - '-s {settings_base} -e SHOW_DRAFTS="true"'.format(**CONFIG)) - - -@task -def reserve(c): - """`build`, then `serve`""" - build(c) - serve(c) - - -@task -def preview(c): - """Build production version of site""" - pelican_run('-s {settings_publish}'.format(**CONFIG)) - - -@task -def livereload(c): - """Automatically reload browser tab upon file modification.""" - from livereload import Server - - def cached_build(): - cmd = '-s {settings_base} -e CACHE_CONTENT=True LOAD_CONTENT_CACHE=True' - pelican_run(cmd.format(**CONFIG)) - - cached_build() - server = Server() - theme_path = SETTINGS['THEME'] - watched_globs = [ - CONFIG['settings_base'], - '{}/templates/**/*.html'.format(theme_path), - ] - - content_file_extensions = ['.md', '.rst'] - for extension in content_file_extensions: - content_glob = '{0}/**/*{1}'.format(SETTINGS['PATH'], extension) - watched_globs.append(content_glob) - - static_file_extensions = ['.css', '.js'] - for extension in static_file_extensions: - static_file_glob = '{0}/static/**/*{1}'.format(theme_path, extension) - watched_globs.append(static_file_glob) - - for glob in watched_globs: - server.watch(glob, cached_build) - - if OPEN_BROWSER_ON_SERVE: - # Open site in default browser - import webbrowser - webbrowser.open("http://{host}:{port}".format(**CONFIG)) - - server.serve(host=CONFIG['host'], port=CONFIG['port'], root=CONFIG['deploy_path']) - - -@task -def publish(c): - """Publish to production via rsync""" - pelican_run('-s {settings_publish}'.format(**CONFIG)) - c.run( - 'rsync --delete --exclude ".DS_Store" -pthrvz -c ' - '-e "ssh -p {ssh_port}" ' - '{} {ssh_user}@{ssh_host}:{ssh_path}'.format( - CONFIG['deploy_path'].rstrip('/') + '/', - **CONFIG)) - - -def pelican_run(cmd): - cmd += ' ' + program.core.remainder # allows to pass-through args to pelican - pelican_main(shlex.split(cmd))