Improve cli, add subcommands and flags.
This commit is contained in:
+89
-5
@@ -1,10 +1,56 @@
|
||||
import argparse
|
||||
import logging
|
||||
import sys
|
||||
from omicron.ssg.site import Site
|
||||
from omicron.ssg.output import OutputError
|
||||
from omicron.ssg.site import ConfigError, Site
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def setup_logging(verbose: bool = False, log_file: str | None = None) -> None:
|
||||
class CliError(RuntimeError):
|
||||
pass
|
||||
|
||||
|
||||
def setup_argparse() -> argparse.ArgumentParser:
|
||||
# base options
|
||||
help_formatter = argparse.ArgumentDefaultsHelpFormatter
|
||||
parser = argparse.ArgumentParser(prog="ossg", formatter_class=help_formatter)
|
||||
parser.add_argument(
|
||||
"--site", type=Path, default=Path("."), help="path to site directory"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--verbose", "-v", action="count", default=0, help="increase log verbosity"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--log-file",
|
||||
type=Path,
|
||||
default=argparse.SUPPRESS,
|
||||
help="write logs to this file (default: <site>/build.log)",
|
||||
)
|
||||
|
||||
subparsers = parser.add_subparsers(dest="command", required=True)
|
||||
|
||||
# build command
|
||||
build = subparsers.add_parser(
|
||||
"build", help="build the site", formatter_class=help_formatter
|
||||
)
|
||||
build.add_argument(
|
||||
"--clean",
|
||||
action=argparse.BooleanOptionalAction,
|
||||
default=True,
|
||||
help="remove output directory before building",
|
||||
)
|
||||
build.add_argument(
|
||||
"--drafts",
|
||||
action=argparse.BooleanOptionalAction,
|
||||
default=False,
|
||||
help="include draft content",
|
||||
)
|
||||
subparsers.add_parser("clean", help="remove the output directory")
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def setup_logging(verbose: bool = False, log_file: Path | None = None) -> None:
|
||||
logger = logging.getLogger("omicron.ssg")
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
@@ -13,8 +59,46 @@ def setup_logging(verbose: bool = False, log_file: str | None = None) -> None:
|
||||
console.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))
|
||||
logger.addHandler(console)
|
||||
|
||||
if log_file is not None:
|
||||
try:
|
||||
file_handler = logging.FileHandler(log_file)
|
||||
except FileNotFoundError as e:
|
||||
raise CliError("log file can't be written") from e
|
||||
file_handler.setLevel(logging.DEBUG)
|
||||
file_handler.setFormatter(
|
||||
logging.Formatter("%(asctime)s %(levelname)s: %(message)s")
|
||||
)
|
||||
logger.addHandler(file_handler)
|
||||
|
||||
|
||||
def build(args: argparse.Namespace) -> None:
|
||||
site = Site(args.site, args.drafts)
|
||||
if args.clean:
|
||||
site.clean()
|
||||
site.build()
|
||||
|
||||
|
||||
def clean(args: argparse.Namespace) -> None:
|
||||
site = Site(args.site)
|
||||
site.clean()
|
||||
|
||||
|
||||
def cli() -> None:
|
||||
parser = setup_argparse()
|
||||
args = parser.parse_args()
|
||||
log_file = getattr(args, "log_file", args.site / "build.log")
|
||||
setup_logging(verbose=bool(args.verbose), log_file=log_file)
|
||||
if args.command == "build":
|
||||
build(args)
|
||||
elif args.command == "clean":
|
||||
clean(args)
|
||||
else:
|
||||
raise NotImplementedError(f"'{args.command}' command not implemented yet")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
setup_logging(verbose=True)
|
||||
site = Site(Path("."))
|
||||
site.build()
|
||||
try:
|
||||
cli()
|
||||
except (ConfigError, OutputError, CliError) as e:
|
||||
print(e)
|
||||
sys.exit(1)
|
||||
|
||||
+13
-3
@@ -1,4 +1,5 @@
|
||||
import logging
|
||||
import shutil
|
||||
from typing import Any, cast
|
||||
from urllib.parse import urlparse
|
||||
|
||||
@@ -31,7 +32,7 @@ class ConfigError(RuntimeError):
|
||||
|
||||
|
||||
class Site:
|
||||
def __init__(self, site: Path):
|
||||
def __init__(self, site: Path, drafts: bool = False):
|
||||
self.site_path = site.resolve()
|
||||
self.content: list[Content] = []
|
||||
self.directories: list[Directory] = []
|
||||
@@ -54,8 +55,11 @@ class Site:
|
||||
def read_config(self) -> dict[str, Any]:
|
||||
config_path = self.site_path / "config.yml"
|
||||
log.debug("Reading config file %s", config_path)
|
||||
with open(config_path, "r") as f:
|
||||
config = yaml.safe_load(f)
|
||||
try:
|
||||
with open(config_path, "r") as f:
|
||||
config = yaml.safe_load(f)
|
||||
except FileNotFoundError:
|
||||
raise ConfigError("config file missing, not a valid ossg directory")
|
||||
if not isinstance(config, dict) or not all(isinstance(k, str) for k in config):
|
||||
raise ConfigError("config file must be a YAML mapping with string keys")
|
||||
if "name" not in config:
|
||||
@@ -188,6 +192,12 @@ class Site:
|
||||
self.other_outputs.append(tags_page)
|
||||
log.info("Discovered %d content items", len(self.content))
|
||||
|
||||
def clean(self) -> None:
|
||||
if not self.output_path.exists():
|
||||
return
|
||||
shutil.rmtree(self.output_path)
|
||||
log.info("Removed %s", self.output_path)
|
||||
|
||||
def build(self) -> None:
|
||||
if not self.origin:
|
||||
log.warning(
|
||||
|
||||
Reference in New Issue
Block a user