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
|
## Working Guidelines
|
||||||
- Keep shared instructions in this file minimal; place deeper guidance in `input/AGENTS.md` or `output/AGENTS.md` as appropriate.
|
- 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.
|
- 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`.
|
- 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)
|
## 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.
|
- 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.
|
- 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.
|
- 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.
|
- 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/outbox:/data/outbox
|
||||||
- ../ForRelease/processed:/data/processed
|
- ../ForRelease/processed:/data/processed
|
||||||
- ../ForRelease/failed:/data/failed
|
- ../ForRelease/failed:/data/failed
|
||||||
- ../../input/templates:/templates:ro
|
- ../templates:/templates:ro
|
||||||
|
|||||||
@@ -20,17 +20,39 @@ OUTBOX = Path("/data/outbox")
|
|||||||
PROCESSED = Path("/data/processed")
|
PROCESSED = Path("/data/processed")
|
||||||
FAILED = Path("/data/failed")
|
FAILED = Path("/data/failed")
|
||||||
TEMPLATES = Path("/templates")
|
TEMPLATES = Path("/templates")
|
||||||
|
TEMPLATE_CACHE = Path("/tmp/templates")
|
||||||
|
|
||||||
DOCX_TEMPLATE = TEMPLATES / "resume-reference.docx"
|
DOCX_TEMPLATE = TEMPLATES / "resume-reference.docx"
|
||||||
|
DOCX_TEMPLATE_EXAMPLE = TEMPLATES / "resume-reference.docx.example"
|
||||||
TEX_TEMPLATE = TEMPLATES / "resume-template.tex"
|
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
|
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:
|
def ensure_environment() -> None:
|
||||||
"""Verify required files and directories exist before processing starts."""
|
"""Verify required files and directories exist before processing starts."""
|
||||||
|
global RESOLVED_DOCX_TEMPLATE, RESOLVED_TEX_TEMPLATE
|
||||||
|
|
||||||
missing = []
|
missing = []
|
||||||
for path in (INBOX, OUTBOX, PROCESSED, FAILED, DOCX_TEMPLATE, TEX_TEMPLATE):
|
for path in (INBOX, OUTBOX, PROCESSED, FAILED, TEMPLATES):
|
||||||
if not path.exists():
|
if not path.exists():
|
||||||
missing.append(str(path))
|
missing.append(str(path))
|
||||||
|
|
||||||
@@ -39,6 +61,9 @@ def ensure_environment() -> None:
|
|||||||
"Required paths are missing inside the container: " + ", ".join(missing)
|
"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:
|
def run_pandoc(input_md: Path, output_docx: Path, output_pdf: Path) -> None:
|
||||||
"""Invoke pandoc twice to create DOCX and PDF artifacts."""
|
"""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",
|
"--to",
|
||||||
"docx",
|
"docx",
|
||||||
"--reference-doc",
|
"--reference-doc",
|
||||||
str(DOCX_TEMPLATE),
|
str(RESOLVED_DOCX_TEMPLATE),
|
||||||
"--output",
|
"--output",
|
||||||
str(output_docx),
|
str(output_docx),
|
||||||
],
|
],
|
||||||
@@ -67,7 +92,7 @@ def run_pandoc(input_md: Path, output_docx: Path, output_pdf: Path) -> None:
|
|||||||
"--pdf-engine",
|
"--pdf-engine",
|
||||||
"xelatex",
|
"xelatex",
|
||||||
"--template",
|
"--template",
|
||||||
str(TEX_TEMPLATE),
|
str(RESOLVED_TEX_TEMPLATE),
|
||||||
"--output",
|
"--output",
|
||||||
str(output_pdf),
|
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/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.
|
- `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.
|
- `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.
|
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
|
## Conversion Flow
|
||||||
1. The watcher polls `ForRelease/inbox` every few seconds for exactly one Markdown resume.
|
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`.
|
3. Artifacts land in a timestamped folder under `ForRelease/outbox`.
|
||||||
4. The source Markdown moves into the matching timestamp folder under `ForRelease/processed`.
|
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.
|
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
|
## Prerequisites
|
||||||
- Docker Engine with the Compose plugin (`docker compose`) or the standalone `docker-compose` binary.
|
- 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:
|
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