refactor(output): relocate templates with examples
This commit is contained in:
@@ -10,5 +10,5 @@ Agents should treat these areas independently so changes can be reasoned about a
|
||||
## Working Guidelines
|
||||
- Keep shared instructions in this file minimal; place deeper guidance in `input/AGENTS.md` or `output/AGENTS.md` as appropriate.
|
||||
- When making automated edits, avoid touching both `input/` and `output/` in the same change set unless the work explicitly spans both pipelines.
|
||||
- Resume conversion templates live under `input/templates`. Output services mount them read-only; update templates from the input side and verify with a fresh conversion run.
|
||||
- Resume conversion templates live under `output/templates`. Copy the `.example` files to matching names (without the suffix) when you need local overrides, and verify with a fresh conversion run.
|
||||
- Use conventional commits (`<type>(scope): <message>`) to signal which side of the system a change targets, e.g., `feat(output): add failed-processing bucket`.
|
||||
|
||||
@@ -20,7 +20,7 @@ Operate the output pipeline that turns one approved Markdown resume at a time in
|
||||
## Guardrails (Do Not Cross)
|
||||
- Treat `ForRelease/outbox` and `ForRelease/processed` as immutable history managed exclusively by the human. Only append new artifacts when explicitly directed; never delete, move, rename, reorganize, or even contemplate cleanup, retention, or consolidation actions.
|
||||
- Refrain from generating additional files outside the described workflow. All job-specific Markdown arrives from the human, and all cleanup decisions belong to the human.
|
||||
- Leave shared templates mounted from `input/templates` untouched; template maintenance happens in the input system.
|
||||
- Leave shared templates under `templates/` untouched unless the human provides explicit replacements (copy `.example` files to matching names without the suffix to override).
|
||||
- Do not queue multiple Markdown files or attempt parallel conversions. This pipeline is intentionally single-job at any moment.
|
||||
- Escalate irregularities (missing templates, repeated failures, permission issues) to the human instead of improvising fixes.
|
||||
|
||||
|
||||
@@ -16,4 +16,4 @@ services:
|
||||
- ../ForRelease/outbox:/data/outbox
|
||||
- ../ForRelease/processed:/data/processed
|
||||
- ../ForRelease/failed:/data/failed
|
||||
- ../../input/templates:/templates:ro
|
||||
- ../templates:/templates:ro
|
||||
|
||||
@@ -20,17 +20,39 @@ OUTBOX = Path("/data/outbox")
|
||||
PROCESSED = Path("/data/processed")
|
||||
FAILED = Path("/data/failed")
|
||||
TEMPLATES = Path("/templates")
|
||||
TEMPLATE_CACHE = Path("/tmp/templates")
|
||||
|
||||
DOCX_TEMPLATE = TEMPLATES / "resume-reference.docx"
|
||||
DOCX_TEMPLATE_EXAMPLE = TEMPLATES / "resume-reference.docx.example"
|
||||
TEX_TEMPLATE = TEMPLATES / "resume-template.tex"
|
||||
TEX_TEMPLATE_EXAMPLE = TEMPLATES / "resume-template.tex.example"
|
||||
|
||||
RESOLVED_DOCX_TEMPLATE: Path | None = None
|
||||
RESOLVED_TEX_TEMPLATE: Path | None = None
|
||||
|
||||
POLL_INTERVAL_SECONDS = 5
|
||||
|
||||
|
||||
def resolve_template(primary: Path, example: Path, cache_dir: Path) -> Path:
|
||||
"""Return the template path, copying .example into a writable cache if needed."""
|
||||
if primary.exists():
|
||||
return primary
|
||||
|
||||
if example.exists():
|
||||
cache_dir.mkdir(parents=True, exist_ok=True)
|
||||
cached = cache_dir / primary.name
|
||||
shutil.copy(example, cached)
|
||||
return cached
|
||||
|
||||
raise FileNotFoundError(f"Template missing: {primary} (no example found)")
|
||||
|
||||
|
||||
def ensure_environment() -> None:
|
||||
"""Verify required files and directories exist before processing starts."""
|
||||
global RESOLVED_DOCX_TEMPLATE, RESOLVED_TEX_TEMPLATE
|
||||
|
||||
missing = []
|
||||
for path in (INBOX, OUTBOX, PROCESSED, FAILED, DOCX_TEMPLATE, TEX_TEMPLATE):
|
||||
for path in (INBOX, OUTBOX, PROCESSED, FAILED, TEMPLATES):
|
||||
if not path.exists():
|
||||
missing.append(str(path))
|
||||
|
||||
@@ -39,6 +61,9 @@ def ensure_environment() -> None:
|
||||
"Required paths are missing inside the container: " + ", ".join(missing)
|
||||
)
|
||||
|
||||
RESOLVED_DOCX_TEMPLATE = resolve_template(DOCX_TEMPLATE, DOCX_TEMPLATE_EXAMPLE, TEMPLATE_CACHE)
|
||||
RESOLVED_TEX_TEMPLATE = resolve_template(TEX_TEMPLATE, TEX_TEMPLATE_EXAMPLE, TEMPLATE_CACHE)
|
||||
|
||||
|
||||
def run_pandoc(input_md: Path, output_docx: Path, output_pdf: Path) -> None:
|
||||
"""Invoke pandoc twice to create DOCX and PDF artifacts."""
|
||||
@@ -51,7 +76,7 @@ def run_pandoc(input_md: Path, output_docx: Path, output_pdf: Path) -> None:
|
||||
"--to",
|
||||
"docx",
|
||||
"--reference-doc",
|
||||
str(DOCX_TEMPLATE),
|
||||
str(RESOLVED_DOCX_TEMPLATE),
|
||||
"--output",
|
||||
str(output_docx),
|
||||
],
|
||||
@@ -67,7 +92,7 @@ def run_pandoc(input_md: Path, output_docx: Path, output_pdf: Path) -> None:
|
||||
"--pdf-engine",
|
||||
"xelatex",
|
||||
"--template",
|
||||
str(TEX_TEMPLATE),
|
||||
str(RESOLVED_TEX_TEMPLATE),
|
||||
"--output",
|
||||
str(output_pdf),
|
||||
],
|
||||
|
||||
@@ -8,6 +8,7 @@ This directory houses the post-processing side of ResumeCustomizer. It accepts o
|
||||
- `ForRelease/processed/YYYY/MM/DD/HHMM` – timestamped archives of Markdown files that converted successfully.
|
||||
- `ForRelease/failed` – holding area for Markdown files that Pandoc could not render.
|
||||
- `Docker/` – Dockerfile, compose stack, watcher script, and wrapper used to run the conversion container.
|
||||
- `templates/` – default Pandoc assets (`*.example`) the watcher copies into `/tmp` at runtime; copy any `.example` file without the suffix to override it locally (ignored by git).
|
||||
|
||||
All `ForRelease` subdirectories include `.gitkeep` and `.gitignore` so artifacts stay local and never reach version control.
|
||||
|
||||
@@ -23,7 +24,7 @@ The wrapper auto-detects the Docker Compose plugin or legacy `docker-compose`, f
|
||||
|
||||
## Conversion Flow
|
||||
1. The watcher polls `ForRelease/inbox` every few seconds for exactly one Markdown resume.
|
||||
2. Pandoc renders DOCX and PDF using the shared templates.
|
||||
2. Pandoc renders DOCX and PDF using the shared templates (and auto-falls back to the bundled `.example` files if no overrides exist).
|
||||
3. Artifacts land in a timestamped folder under `ForRelease/outbox`.
|
||||
4. The source Markdown moves into the matching timestamp folder under `ForRelease/processed`.
|
||||
5. On Pandoc failure, the Markdown shifts into `ForRelease/failed` for human review before retrying.
|
||||
@@ -32,7 +33,7 @@ Outbox and processed directories are append-only historical records managed sole
|
||||
|
||||
## Prerequisites
|
||||
- Docker Engine with the Compose plugin (`docker compose`) or the standalone `docker-compose` binary.
|
||||
- Template assets mounted read-only from `input/templates`.
|
||||
- Template assets mounted read-only from `templates/` (copy `.example` files to matching names without the suffix to customize them).
|
||||
|
||||
Stop or inspect the stack with:
|
||||
|
||||
|
||||
4
output/templates/.gitignore
vendored
Normal file
4
output/templates/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
*.docx
|
||||
*.tex
|
||||
!*.docx.example
|
||||
!*.tex.example
|
||||
Reference in New Issue
Block a user