diff --git a/.gitignore b/.gitignore index ca7b725..55d8e59 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ build/ # Report report.md +output/ diff --git a/Dockerfile b/Dockerfile index 57c8a28..d06e4c8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,8 +20,9 @@ RUN cd /tmp && \ fc-cache -f && \ rm -f /tmp/*.TTF /tmp/times32.exe -WORKDIR /app +WORKDIR /build COPY . . -RUN uv sync --frozen --no-dev +RUN uv pip install --system --no-cache . -ENTRYPOINT ["uv", "run", "--no-dev", "md2gost"] +ENV UV_CACHE_DIR=/tmp/uv-cache +ENTRYPOINT ["md2gost"] diff --git a/md2gost.sh b/md2gost.sh index f579a2d..9198e09 100755 --- a/md2gost.sh +++ b/md2gost.sh @@ -4,12 +4,67 @@ set -euo pipefail IMAGE="md2gost" SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +usage() { + echo "Использование: md2gost file.md [-o dir]" + echo " file.md — исходный Markdown-файл" + echo " -o dir — выходная директория (по умолчанию ./build)" + exit 1 +} + +# Парсим аргументы +INPUT_FILE="" +OUTPUT_DIR="./build" + +while [[ $# -gt 0 ]]; do + case "$1" in + -o) + OUTPUT_DIR="${2:-}" + [[ -z "$OUTPUT_DIR" ]] && usage + shift 2 + ;; + -h|--help) + usage + ;; + -*) + echo "неизвестный флаг: $1" >&2 + usage + ;; + *) + INPUT_FILE="$1" + shift + ;; + esac +done + +[[ -z "$INPUT_FILE" ]] && usage + +# Проверяем входной файл +if [[ ! -f "$INPUT_FILE" ]]; then + echo "ошибка: $INPUT_FILE не найден" >&2 + exit 1 +fi + +# Абсолютные пути +INPUT_ABS="$(realpath "$INPUT_FILE")" +OUTPUT_ABS="$(realpath -m "$OUTPUT_DIR")" +mkdir -p "$OUTPUT_ABS" + +INPUT_BASE="$(basename "$INPUT_FILE")" + +# Собираем образ если нужно if ! docker image inspect "$IMAGE" &>/dev/null; then - echo " образ $IMAGE не найден, собираю..." + echo "образ $IMAGE не найден, собираю..." docker build -t "$IMAGE" "$SCRIPT_DIR" fi +echo "→ конвертация $INPUT_FILE → $OUTPUT_DIR/" + +# Запуск контейнера: +# - вход: только .md файл (read-only) +# - выход: директория для .tex и .pdf +# - user: текущий пользователь (не root) docker run --rm \ - -e UV_CACHE_DIR=/tmp/uv-cache \ - -v "$(pwd):/app" \ - "$IMAGE" "$@" + -v "$INPUT_ABS:/input/$INPUT_BASE:ro" \ + -v "$OUTPUT_ABS:/output" \ + --user "$(id -u):$(id -g)" \ + "$IMAGE" "/input/$INPUT_BASE" -o /output diff --git a/md2gost/main.py b/md2gost/main.py index 79cb35c..968c193 100644 --- a/md2gost/main.py +++ b/md2gost/main.py @@ -1,6 +1,8 @@ import argparse +import shutil import subprocess import sys +import tempfile from pathlib import Path from md2gost.parser import Parser @@ -44,27 +46,41 @@ def main(): renderer = Renderer(parser.tokens, parser.front_matter) latex = renderer.render() - tex_path = args.output / args.input.with_suffix(".tex").name - tex_path.write_text(latex, encoding="utf-8") - print(f"→ {tex_path}") + # Собираем во временную директорию, копируем только .tex и .pdf + with tempfile.TemporaryDirectory(prefix="md2gost-") as cache: + cache_dir = Path(cache) - if not args.no_pdf: - pdf_path = args.output / args.input.with_suffix(".pdf").name - log_path = args.output / "report.log" + # Генерируем .tex в кэш + tex_name = args.input.with_suffix(".tex").name + tex_path = cache_dir / tex_name + tex_path.write_text(latex, encoding="utf-8") - if not run_xelatex(tex_path, args.output): - print("ошибка: xelatex не собрал PDF", file=sys.stderr) - if log_path.exists(): - for line in log_path.read_text(errors="replace").splitlines(): - if line.startswith("!"): - print(f" {line}", file=sys.stderr) - sys.exit(1) + # Копируем .tex в выходную директорию + final_tex = args.output / tex_name + shutil.copy2(tex_path, final_tex) + print(f"→ {final_tex}") - if not run_xelatex(tex_path, args.output): - print("ошибка: xelatex (2-й проход) не собрал PDF", file=sys.stderr) - sys.exit(1) + if not args.no_pdf: + # Компилируем PDF в кэше (два прохода для TOC) + if not run_xelatex(tex_path, cache_dir): + print("ошибка: xelatex не собрал PDF", file=sys.stderr) + log_path = cache_dir / "report.log" + if log_path.exists(): + for line in log_path.read_text(errors="replace").splitlines(): + if line.startswith("!"): + print(f" {line}", file=sys.stderr) + sys.exit(1) - print(f"→ {pdf_path}") + if not run_xelatex(tex_path, cache_dir): + print("ошибка: xelatex (2-й проход) не собрал PDF", file=sys.stderr) + sys.exit(1) + + # Копируем .pdf в выходную директорию + pdf_name = args.input.with_suffix(".pdf").name + pdf_path = cache_dir / pdf_name + final_pdf = args.output / pdf_name + shutil.copy2(pdf_path, final_pdf) + print(f"→ {final_pdf}") if __name__ == "__main__":