Refactoring build system commands

This commit is contained in:
evilchili 2023-07-04 12:35:04 -07:00
parent b96e46b67d
commit 54c63834c7
2 changed files with 170 additions and 125 deletions

View File

@ -0,0 +1,136 @@
from collections import defaultdict
from pathlib import Path
from time import sleep
import os
import subprocess
import shlex
import shutil
import webbrowser
from pelican import main as pelican_main
from livereload import Server
from livereload.watcher import INotifyWatcher
import site_tools as st
CONFIG = defaultdict(dict)
CONFIG.update(
{
"settings_base": st.DEV_SETTINGS_FILE_BASE,
"settings_publish": st.PUB_SETTINGS_FILE_BASE,
# Output path. Can be absolute or relative to tasks.py. Default: 'output'
"deploy_path": st.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",
# directory to watch for new assets
"import_path": "imports",
# where new asseets will be made available
"production_host": "deadsands.froghat.club",
# where to find roll table sources
"table_sources_path": "sources",
}
)
def pelican_run(cmd: list = [], publish=False) -> None:
settings = CONFIG["settings_publish" if publish else "settings_base"]
pelican_main(["-s", settings] + cmd)
def clean():
if os.path.isdir(CONFIG["deploy_path"]):
shutil.rmtree(CONFIG["deploy_path"])
os.makedirs(CONFIG["deploy_path"])
def build():
subprocess.run(shlex.split("git submodule update --remote --merge"))
pelican_run()
def watch():
import_path = Path(CONFIG["import_path"])
content_path = Path(st.SETTINGS["PATH"])
def do_import():
assets = []
for src in import_path.rglob("*"):
relpath = src.relative_to(import_path)
target = content_path / relpath
if src.is_dir():
target.mkdir(parents=True, exist_ok=True)
continue
if target.exists():
print(f"{target}: exists; skipping.")
continue
print(f"{target}: importing...")
src.link_to(target)
subprocess.run(shlex.split(f"git add {target}"))
uri = target.relative_to("content")
assets.append(f"https://{CONFIG['production_host']}/{uri}")
src.unlink()
if assets:
publish()
print("\n\t".join(["\nImported Asset URLS:"] + assets))
print("\n")
watcher = INotifyWatcher()
watcher.watch(import_path, do_import)
watcher.start(do_import)
print(f"Watching {import_path}. CTRL+C to exit.")
while True:
watcher.examine()
sleep(5)
def serve():
url = "http://{host}:{port}/".format(**CONFIG)
def cached_build():
pelican_run(["-ve", "CACHE_CONTENT=true", "LOAD_CONTENT_CACHE=true", "SHOW_DRAFTS=true", f'SITEURL="{url}"'])
clean()
cached_build()
server = Server()
theme_path = st.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(st.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 st.OPEN_BROWSER_ON_SERVE:
webbrowser.open(url)
server.serve(host=CONFIG["host"], port=CONFIG["port"], root=CONFIG["deploy_path"])
def publish():
clean()
pelican_run(publish=True)
subprocess.run(
shlex.split(
'rsync --delete --exclude ".DS_Store" -pthrvz -c '
'-e "ssh -p {ssh_port}" '
"{} {ssh_user}@{ssh_host}:{ssh_path}".format(CONFIG["deploy_path"].rstrip("/") + "/", **CONFIG)
)
)

View File

@ -1,55 +1,18 @@
import asyncio import asyncio
import os
import shlex
import shutil
import subprocess
import sys import sys
import termios import termios
import webbrowser
from collections import defaultdict
from enum import Enum from enum import Enum
from pathlib import Path from pathlib import Path
from time import sleep
import click import click
import typer import typer
from livereload import Server
from livereload.watcher import INotifyWatcher
from pelican import main as pelican_main
from rolltable.tables import RollTable from rolltable.tables import RollTable
from typing_extensions import Annotated from typing_extensions import Annotated
import site_tools as st
from site_tools.content_manager import create from site_tools.content_manager import create
from site_tools.shell.interactive_shell import DMShell from site_tools.shell.interactive_shell import DMShell
from site_tools import build_system
CONFIG = defaultdict(dict)
CONFIG.update(
{
"settings_base": st.DEV_SETTINGS_FILE_BASE,
"settings_publish": st.PUB_SETTINGS_FILE_BASE,
# Output path. Can be absolute or relative to tasks.py. Default: 'output'
"deploy_path": st.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",
# directory to watch for new assets
"import_path": "imports",
# where new asseets will be made available
"production_host": "deadsands.froghat.club",
# where to find roll table sources
"table_sources_path": "sources",
}
)
app = typer.Typer()
class ContentType(str, Enum): class ContentType(str, Enum):
post = "post" post = "post"
@ -59,6 +22,7 @@ class ContentType(str, Enum):
location = "location" location = "location"
page = "page" page = "page"
class Die(str, Enum): class Die(str, Enum):
d100 = "100" d100 = "100"
d20 = "20" d20 = "20"
@ -67,101 +31,41 @@ class Die(str, Enum):
d6 = "6" d6 = "6"
d4 = "4" d4 = "4"
def pelican_run(cmd: list = [], publish=False) -> None:
settings = CONFIG["settings_publish" if publish else "settings_base"]
pelican_main(["-s", settings] + cmd)
@app.command() # 'site' ENTRY POINT
site_app = typer.Typer()
# BUILD SYSTEM SUBCOMMANDS
@site_app.command()
def clean() -> None: def clean() -> None:
if os.path.isdir(CONFIG["deploy_path"]): build_system.clean()
shutil.rmtree(CONFIG["deploy_path"])
os.makedirs(CONFIG["deploy_path"])
@app.command()
@site_app.command()
def build() -> None: def build() -> None:
subprocess.run(shlex.split("git submodule update --remote --merge")) build_system.build()
pelican_run()
@app.command()
@site_app.command()
def watch() -> None: def watch() -> None:
import_path = Path(CONFIG["import_path"]) build_system.watch()
content_path = Path(st.SETTINGS["PATH"])
def do_import():
assets = []
for src in import_path.rglob("*"):
relpath = src.relative_to(import_path)
target = content_path / relpath
if src.is_dir():
target.mkdir(parents=True, exist_ok=True)
continue
if target.exists():
print(f"{target}: exists; skipping.")
continue
print(f"{target}: importing...")
src.link_to(target)
subprocess.run(shlex.split(f"git add {target}"))
uri = target.relative_to("content")
assets.append(f"https://{CONFIG['production_host']}/{uri}")
src.unlink()
if assets:
publish()
print("\n\t".join(["\nImported Asset URLS:"] + assets))
print("\n")
watcher = INotifyWatcher()
watcher.watch(import_path, do_import)
watcher.start(do_import)
print(f"Watching {import_path}. CTRL+C to exit.")
while True:
watcher.examine()
sleep(5)
@app.command() @site_app.command()
def serve() -> None: def serve() -> None:
url = "http://{host}:{port}/".format(**CONFIG) build_system.serve()
def cached_build():
pelican_run(["-ve", "CACHE_CONTENT=true", "LOAD_CONTENT_CACHE=true", "SHOW_DRAFTS=true", f'SITEURL="{url}"'])
clean()
cached_build()
server = Server()
theme_path = st.SETTINGS["THEME"]
watched_globs = [
CONFIG["settings_base"],
"{}/templates/**/*.html".format(theme_path),
]
content_file_extensions = [".md", ".rst"] @site_app.command()
for extension in content_file_extensions:
content_glob = "{0}/**/*{1}".format(st.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 st.OPEN_BROWSER_ON_SERVE:
webbrowser.open(url)
server.serve(host=CONFIG["host"], port=CONFIG["port"], root=CONFIG["deploy_path"])
@app.command()
def publish() -> None: def publish() -> None:
clean() build_system.publish()
pelican_run(publish=True)
subprocess.run(
shlex.split(
'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()
# CONTENT MANAGEMENT COMMANDS
@site_app.command()
def restock( def restock(
source: str = typer.Argument(..., help="The source file for the store."), source: str = typer.Argument(..., help="The source file for the store."),
frequency: str = Annotated[str, typer.Option("default", help="use the specified frequency from the source file")], frequency: str = Annotated[str, typer.Option("default", help="use the specified frequency from the source file")],
@ -169,7 +73,7 @@ def restock(
template_dir: str = Annotated[ template_dir: str = Annotated[
str, str,
typer.Argument( typer.Argument(
CONFIG["templates_path"], build_system.CONFIG["templates_path"],
help="Override the default location for markdown templates.", help="Override the default location for markdown templates.",
), ),
], ],
@ -188,7 +92,8 @@ def restock(
) )
) )
@app.command()
@site_app.command()
def new( def new(
content_type: ContentType = typer.Argument( content_type: ContentType = typer.Argument(
..., ...,
@ -207,7 +112,7 @@ def new(
help="Override the default template for the content_type.", help="Override the default template for the content_type.",
), ),
template_dir: str = typer.Argument( template_dir: str = typer.Argument(
CONFIG["templates_path"], build_system.CONFIG["templates_path"],
help="Override the default location for markdown templates.", help="Override the default location for markdown templates.",
), ),
) -> None: ) -> None:
@ -228,12 +133,16 @@ def new(
category = content_type.value category = content_type.value
click.edit(filename=create(content_type.value, title, template_dir, category, template or content_type.value)) click.edit(filename=create(content_type.value, title, template_dir, category, template or content_type.value))
# STANDALONE ENTRY POINTS
def dmsh(): def dmsh():
old_attrs = termios.tcgetattr(sys.stdin) old_attrs = termios.tcgetattr(sys.stdin)
try: try:
asyncio.run(DMShell(CONFIG).start()) asyncio.run(DMShell(build_system.CONFIG).start())
finally: finally:
termios.tcsetattr(sys.stdin, termios.TCSANOW, old_attrs) termios.tcsetattr(sys.stdin, termios.TCSANOW, old_attrs)
if __name__ == "__main__": if __name__ == "__main__":
app() site_app()