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:
+27
-57
@@ -6,19 +6,19 @@ Provides the primary entry point for programmatic gallery generation.
|
||||
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
from typing import Union, List, Dict, Any
|
||||
from typing import Any, Dict, List, Optional, Union, cast
|
||||
|
||||
from gallery.builder import build_gallery, copy_assets, get_template
|
||||
from gallery.config import GalleryConfig, GallerySource
|
||||
from gallery.builder import get_template, build_gallery, copy_assets
|
||||
|
||||
|
||||
def generate(
|
||||
config: Union[GalleryConfig, str, Path] = None,
|
||||
web_folder: Union[str, Path] = None,
|
||||
sources: List[Union[GallerySource, Dict[str, Any]]] = None,
|
||||
config: Optional[Union[GalleryConfig, str, Path]] = None,
|
||||
web_folder: Optional[Union[str, Path]] = None,
|
||||
sources: Optional[List[Union[GallerySource, Dict[str, Any]]]] = None,
|
||||
clean_first: bool = False,
|
||||
verbose: bool = False,
|
||||
source_to_update: GallerySource = None,
|
||||
source_to_update: Optional[GallerySource] = None,
|
||||
) -> bool:
|
||||
"""
|
||||
Generate a scientific gallery from plot sources.
|
||||
@@ -79,20 +79,11 @@ def generate(
|
||||
if isinstance(config, (str, Path)):
|
||||
config = GalleryConfig.from_yaml(config)
|
||||
elif not isinstance(config, GalleryConfig):
|
||||
raise TypeError(
|
||||
f"config must be GalleryConfig, str, or Path, "
|
||||
f"got {type(config)}"
|
||||
)
|
||||
raise TypeError(f"config must be GalleryConfig, str, or Path, got {type(config)}")
|
||||
else:
|
||||
if web_folder is None or sources is None:
|
||||
raise ValueError(
|
||||
"Either config or both web_folder and sources "
|
||||
"must be provided"
|
||||
)
|
||||
config = GalleryConfig(
|
||||
web_folder=web_folder,
|
||||
sources=sources or []
|
||||
)
|
||||
raise ValueError("Either config or both web_folder and sources must be provided")
|
||||
config = GalleryConfig(web_folder=web_folder, sources=sources or [])
|
||||
|
||||
# Validate configuration
|
||||
if not config.sources:
|
||||
@@ -104,10 +95,7 @@ def generate(
|
||||
web_folder_path = Path(config.web_folder)
|
||||
if not _is_writable(web_folder_path):
|
||||
if verbose:
|
||||
print(
|
||||
f"Error: Cannot write to web_folder: "
|
||||
f"{config.web_folder}"
|
||||
)
|
||||
print(f"Error: Cannot write to web_folder: {config.web_folder}")
|
||||
return False
|
||||
|
||||
# Create gallery root directory
|
||||
@@ -127,17 +115,12 @@ def generate(
|
||||
source_subdir = gallery_root / source_to_update.name
|
||||
if source_subdir.exists():
|
||||
if verbose:
|
||||
print(
|
||||
f"Updating source directory {source_to_update.name}..."
|
||||
)
|
||||
print(f"Updating source directory {source_to_update.name}...")
|
||||
try:
|
||||
shutil.rmtree(source_subdir)
|
||||
except Exception as e:
|
||||
if verbose:
|
||||
print(
|
||||
f"Warning: Could not clean source subdirectory "
|
||||
f"{source_subdir}: {e}"
|
||||
)
|
||||
print(f"Warning: Could not clean source subdirectory {source_subdir}: {e}")
|
||||
return False
|
||||
|
||||
try:
|
||||
@@ -162,8 +145,10 @@ def generate(
|
||||
return False
|
||||
|
||||
# Process sources
|
||||
# config.sources is always List[GallerySource] after GalleryConfig.__post_init__ normalizes it.
|
||||
source_subdirs = []
|
||||
for source in config.sources:
|
||||
source = cast(GallerySource, source)
|
||||
# Skip sources not matching the update target (if specified)
|
||||
if source_to_update and source.name != source_to_update.name:
|
||||
# Still include them in the index if they exist
|
||||
@@ -178,10 +163,7 @@ def generate(
|
||||
# Validate source exists
|
||||
if not source_path.exists():
|
||||
if verbose:
|
||||
print(
|
||||
f"Warning: Source {source.path} does not exist. "
|
||||
f"Skipping."
|
||||
)
|
||||
print(f"Warning: Source {source.path} does not exist. Skipping.")
|
||||
continue
|
||||
|
||||
source_web_dir = gallery_root / source.name
|
||||
@@ -189,47 +171,37 @@ def generate(
|
||||
source_web_dir.mkdir(parents=True, exist_ok=True)
|
||||
except Exception as e:
|
||||
if verbose:
|
||||
print(
|
||||
f"Warning: Could not create directory "
|
||||
f"{source_web_dir}: {e}"
|
||||
)
|
||||
print(f"Warning: Could not create directory {source_web_dir}: {e}")
|
||||
continue
|
||||
|
||||
source_subdirs.append(source.name)
|
||||
|
||||
# Process source
|
||||
if source_path.is_file() and source_path.suffix == '.pdf':
|
||||
if source_path.is_file() and source_path.suffix == ".pdf":
|
||||
# Single PDF file
|
||||
from gallery.utils.processing import process_plot_files
|
||||
|
||||
item = process_plot_files(
|
||||
config=config,
|
||||
plot_file=source_path,
|
||||
web_dir=source_web_dir,
|
||||
)
|
||||
from gallery.utils.processing import render_gallery_page
|
||||
|
||||
render_gallery_page(
|
||||
config=config,
|
||||
template=template,
|
||||
web_dir=source_web_dir,
|
||||
items=[item],
|
||||
subdirs=[],
|
||||
relative_path=Path(source.name)
|
||||
relative_path=Path(source.name),
|
||||
)
|
||||
elif source_path.is_dir():
|
||||
# Directory of plots
|
||||
build_gallery(
|
||||
config,
|
||||
source_path,
|
||||
source_web_dir,
|
||||
template,
|
||||
Path(source.name)
|
||||
)
|
||||
build_gallery(config, source_path, source_web_dir, template, Path(source.name))
|
||||
else:
|
||||
if verbose:
|
||||
print(
|
||||
f"Warning: Source {source.path} is neither a "
|
||||
f"directory nor a PDF file. Skipping."
|
||||
)
|
||||
print(f"Warning: Source {source.path} is neither a directory nor a PDF file. Skipping.")
|
||||
continue
|
||||
|
||||
if verbose:
|
||||
@@ -237,15 +209,13 @@ def generate(
|
||||
|
||||
except Exception as e:
|
||||
if verbose:
|
||||
print(
|
||||
f"Warning: Error processing source "
|
||||
f"{source.name}: {e}"
|
||||
)
|
||||
print(f"Warning: Error processing source {source.name}: {e}")
|
||||
continue
|
||||
|
||||
# Render gallery root index
|
||||
try:
|
||||
from gallery.utils.processing import render_gallery_page
|
||||
|
||||
render_gallery_page(
|
||||
config=config,
|
||||
template=template,
|
||||
@@ -253,7 +223,7 @@ def generate(
|
||||
items=[],
|
||||
subdirs=source_subdirs,
|
||||
relative_path=Path("."),
|
||||
title="Gallery Root"
|
||||
title="Gallery Root",
|
||||
)
|
||||
except Exception as e:
|
||||
if verbose:
|
||||
@@ -277,8 +247,8 @@ def generate(
|
||||
def _count_gallery_plots(gallery_root: Path) -> int:
|
||||
"""Recursively count plot files (PDFs and HTMLs, excluding index.html) in the gallery output."""
|
||||
count = 0
|
||||
for f in gallery_root.rglob('*'):
|
||||
if f.is_file() and f.suffix.lower() in ('.pdf', '.html') and f.name != 'index.html':
|
||||
for f in gallery_root.rglob("*"):
|
||||
if f.is_file() and f.suffix.lower() in (".pdf", ".html") and f.name != "index.html":
|
||||
count += 1
|
||||
return count
|
||||
|
||||
|
||||
Reference in New Issue
Block a user