Rewrite CI to lint/typecheck/audit/test only; add HPC deployment path
- Replace the Docker build/publish CI stages with ruff (lint + format check), ty (type check), pip-audit, and pytest run directly against python:3.11-slim; Docker remains for manual/server deployment only. - Swap black/pylint/mypy for ruff/ty across pyproject.toml, and fix every resulting lint, format, and type diagnostic in gallery/ and plotstyle/. - Fix tests broken/stale from before the package restructuring: wrong `utils.*` import paths, mock patch targets pointed at the wrong module, and PDF-conversion tests still assuming ImageMagick instead of the current PyMuPDF-first path. Drop test_container.py (obsolete Docker-container smoke tests, fully superseded elsewhere). - Add a plain-venv + systemd --user timer deployment path (deploy/systemd/) for HPC login nodes without a Docker daemon, where public_html is already served by existing infrastructure. - Document both in CLAUDE.md, including running CI's checks locally before committing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+42
-23
@@ -10,13 +10,17 @@ import argparse
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import List, Optional, cast
|
||||
|
||||
import argcomplete
|
||||
|
||||
from gallery import generate
|
||||
from gallery.config import (
|
||||
ConfigManager, GalleryConfig, GallerySource,
|
||||
default_config_path, get_active_config_path, ensure_user_config,
|
||||
ConfigManager,
|
||||
GalleryConfig,
|
||||
GallerySource,
|
||||
ensure_user_config,
|
||||
get_active_config_path,
|
||||
)
|
||||
|
||||
_WELCOME = """\
|
||||
@@ -58,6 +62,11 @@ Config file: {config_path}
|
||||
"""
|
||||
|
||||
|
||||
def _set_completer(action: argparse.Action, completer) -> None:
|
||||
"""argcomplete reads `.completer` dynamically; argparse.Action has no such attribute."""
|
||||
setattr(action, "completer", completer)
|
||||
|
||||
|
||||
def _is_configured(config_path: Path) -> bool:
|
||||
"""Return True if web_folder is set to a non-empty value."""
|
||||
try:
|
||||
@@ -104,13 +113,16 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
description="Scientific Plot Gallery Generator",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--config",
|
||||
type=str,
|
||||
default=None,
|
||||
metavar="FILE",
|
||||
help="Path to config file (default: package bundled config)",
|
||||
).completer = argcomplete.completers.FilesCompleter(["yaml", "yml"])
|
||||
_set_completer(
|
||||
parser.add_argument(
|
||||
"--config",
|
||||
type=str,
|
||||
default=None,
|
||||
metavar="FILE",
|
||||
help="Path to config file (default: package bundled config)",
|
||||
),
|
||||
argcomplete.completers.FilesCompleter(["yaml", "yml"]),
|
||||
)
|
||||
|
||||
sub = parser.add_subparsers(dest="command", metavar="COMMAND")
|
||||
|
||||
@@ -121,13 +133,16 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
description="Generate scientific gallery from plot collections",
|
||||
)
|
||||
gen.add_argument("--clean", action="store_true", help="Clean gallery directory before generation")
|
||||
gen.add_argument(
|
||||
"--source",
|
||||
type=str,
|
||||
default=None,
|
||||
metavar="DIR",
|
||||
help="Only recompute a specific source directory (name defaults to dir name)",
|
||||
).completer = argcomplete.completers.DirectoriesCompleter()
|
||||
_set_completer(
|
||||
gen.add_argument(
|
||||
"--source",
|
||||
type=str,
|
||||
default=None,
|
||||
metavar="DIR",
|
||||
help="Only recompute a specific source directory (name defaults to dir name)",
|
||||
),
|
||||
argcomplete.completers.DirectoriesCompleter(),
|
||||
)
|
||||
gen.add_argument("-v", "--verbose", action="store_true", help="Print verbose output")
|
||||
|
||||
# --- config -------------------------------------------------------------
|
||||
@@ -146,20 +161,21 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
cfg_sub.add_parser("sources", help="List all configured sources")
|
||||
|
||||
p_get = cfg_sub.add_parser("get", help="Get a config value")
|
||||
p_get.add_argument("key", help="Dot-separated key, e.g. gallery.png_dpi").completer = _config_keys
|
||||
_set_completer(p_get.add_argument("key", help="Dot-separated key, e.g. gallery.png_dpi"), _config_keys)
|
||||
|
||||
p_set = cfg_sub.add_parser("set", help="Set a config value")
|
||||
p_set.add_argument("key", help="Dot-separated key, e.g. gallery.png_dpi").completer = _config_keys
|
||||
_set_completer(p_set.add_argument("key", help="Dot-separated key, e.g. gallery.png_dpi"), _config_keys)
|
||||
p_set.add_argument("value", help="New value (YAML-parsed: use true/false for bools)")
|
||||
|
||||
p_add = cfg_sub.add_parser("add-source", help="Add a plot source")
|
||||
p_add.add_argument("--name", default=None, help="Source name (default: bottom-level directory name)")
|
||||
p_add.add_argument("--path", required=True, metavar="DIR", help="Path to source directory").completer = (
|
||||
argcomplete.completers.DirectoriesCompleter()
|
||||
_set_completer(
|
||||
p_add.add_argument("--path", required=True, metavar="DIR", help="Path to source directory"),
|
||||
argcomplete.completers.DirectoriesCompleter(),
|
||||
)
|
||||
|
||||
p_rm = cfg_sub.add_parser("remove-source", help="Remove a plot source by name")
|
||||
p_rm.add_argument("name", help="Source name to remove").completer = _source_names
|
||||
_set_completer(p_rm.add_argument("name", help="Source name to remove"), _source_names)
|
||||
|
||||
return parser
|
||||
|
||||
@@ -174,10 +190,12 @@ def _run_generate(args: argparse.Namespace) -> int:
|
||||
try:
|
||||
config = GalleryConfig.from_yaml(config_path)
|
||||
|
||||
source_to_update = None
|
||||
source_to_update: Optional[GallerySource] = None
|
||||
if args.source:
|
||||
source_path = Path(args.source).resolve()
|
||||
matching = next((s for s in config.sources if Path(s.path).resolve() == source_path), None)
|
||||
# config.sources is always List[GallerySource] after GalleryConfig.__post_init__ normalizes it.
|
||||
typed_sources = cast(List[GallerySource], config.sources)
|
||||
matching = next((s for s in typed_sources if Path(s.path).resolve() == source_path), None)
|
||||
if matching is None:
|
||||
source_to_update = GallerySource(name=source_path.name, path=source_path)
|
||||
config.sources.append(source_to_update)
|
||||
@@ -312,6 +330,7 @@ def main():
|
||||
sys.exit(_run_install_completion())
|
||||
elif args.command == "tui":
|
||||
from gallery.tui import GalleryTUI
|
||||
|
||||
config_path = Path(args.config) if args.config else None
|
||||
GalleryTUI(config_path=config_path).run()
|
||||
sys.exit(0)
|
||||
|
||||
Reference in New Issue
Block a user