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:
2026-07-22 15:29:55 +02:00
parent 4ba321b327
commit 3b9d1ef1a8
27 changed files with 752 additions and 915 deletions
+39 -38
View File
@@ -3,34 +3,31 @@
import shutil
import subprocess
from pathlib import Path
from typing import Any, Dict
from typing import Any, Dict, Optional
from jinja2 import Template
from gallery.config import GalleryConfig
from gallery.utils.metadata import (
get_metadata_file_path,
resolve_metadata_for_plot,
)
from gallery.utils.stats import (
calculate_directory_stats,
format_file_size,
)
try:
import fitz # PyMuPDF
_PYMUPDF_AVAILABLE = True
except ImportError:
_PYMUPDF_AVAILABLE = False
_IMAGEMAGICK_AVAILABLE = shutil.which("convert") is not None
from jinja2 import Template
from gallery.utils.metadata import (
resolve_metadata_for_plot,
get_metadata_file_path,
)
from gallery.utils.stats import (
calculate_directory_stats,
format_file_size,
)
from gallery.config import GalleryConfig
def process_html_file(
html_file: Path,
web_dir: Path,
current_metadata: Dict[str, Any] = None
) -> dict:
def process_html_file(html_file: Path, web_dir: Path, current_metadata: Optional[Dict[str, Any]] = None) -> dict:
"""
Process HTML plot file, copying it to web directory.
@@ -60,15 +57,15 @@ def process_html_file(
"html_href": html_file.name,
"is_html": True,
"metadata": plot_metadata,
"creation_time": source_creation_time
"creation_time": source_creation_time,
}
def process_plot_files(
config: GalleryConfig,
plot_file: Path,
web_dir: Path,
current_metadata: Dict[str, Any] = None,
config: GalleryConfig,
plot_file: Path,
web_dir: Path,
current_metadata: Optional[Dict[str, Any]] = None,
) -> dict:
"""
Process plot files (PDF/PNG or HTML), handling conversion and copying.
@@ -82,7 +79,7 @@ def process_plot_files(
Returns:
Dictionary containing plot information
"""
if plot_file.suffix.lower() == '.html':
if plot_file.suffix.lower() == ".html":
return process_html_file(plot_file, web_dir, current_metadata)
# Handle PDF files
@@ -111,7 +108,7 @@ def process_plot_files(
"png_href": png_file.name,
"is_html": False,
"metadata": plot_metadata,
"creation_time": source_creation_time
"creation_time": source_creation_time,
}
@@ -122,8 +119,8 @@ def render_gallery_page(
items: list,
subdirs: list,
relative_path: Path,
title: str = None,
metadata: dict = None
title: Optional[str] = None,
metadata: Optional[dict] = None,
) -> None:
"""
Unified template rendering for all gallery pages.
@@ -139,8 +136,7 @@ def render_gallery_page(
metadata: Metadata dictionary (optional)
"""
if title is None:
title = "Gallery" if relative_path == Path(
".") else f"Gallery: {relative_path}"
title = "Gallery" if relative_path == Path(".") else f"Gallery: {relative_path}"
if metadata is None:
metadata = {}
@@ -151,7 +147,7 @@ def render_gallery_page(
"file_count": len(items),
"folder_count": len(subdirs),
"total_size": format_file_size(current_stats["total_size"]),
"total_size_bytes": current_stats["total_size"]
"total_size_bytes": current_stats["total_size"],
}
# Calculate relative path to assets
@@ -185,7 +181,7 @@ def render_gallery_page(
folder_metadata=metadata,
assets_path=assets_path,
source_dir=str(web_dir),
metadata_file_path=get_metadata_file_path(web_dir)
metadata_file_path=get_metadata_file_path(web_dir),
)
f.write(rendered_html)
@@ -232,13 +228,18 @@ def _convert_pdf_pymupdf(pdf_path: Path, png_path: Path, dpi: int) -> None:
def _convert_pdf_imagemagick(pdf_path: Path, png_path: Path, dpi: int) -> None:
subprocess.run([
"convert",
"-density", str(dpi),
str(pdf_path),
"-quality", "95",
str(png_path),
], check=True)
subprocess.run(
[
"convert",
"-density",
str(dpi),
str(pdf_path),
"-quality",
"95",
str(png_path),
],
check=True,
)
def needs_update(source_file: Path, target_file: Path) -> bool: