[UPDATE] интеграция: безопасные маунты, кэш в tmpdir, только .tex/.pdf
This commit is contained in:
@@ -16,3 +16,4 @@ build/
|
|||||||
|
|
||||||
# Report
|
# Report
|
||||||
report.md
|
report.md
|
||||||
|
output/
|
||||||
|
|||||||
+4
-3
@@ -20,8 +20,9 @@ RUN cd /tmp && \
|
|||||||
fc-cache -f && \
|
fc-cache -f && \
|
||||||
rm -f /tmp/*.TTF /tmp/times32.exe
|
rm -f /tmp/*.TTF /tmp/times32.exe
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /build
|
||||||
COPY . .
|
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"]
|
||||||
|
|||||||
+59
-4
@@ -4,12 +4,67 @@ set -euo pipefail
|
|||||||
IMAGE="md2gost"
|
IMAGE="md2gost"
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
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
|
if ! docker image inspect "$IMAGE" &>/dev/null; then
|
||||||
echo " образ $IMAGE не найден, собираю..."
|
echo "образ $IMAGE не найден, собираю..."
|
||||||
docker build -t "$IMAGE" "$SCRIPT_DIR"
|
docker build -t "$IMAGE" "$SCRIPT_DIR"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo "→ конвертация $INPUT_FILE → $OUTPUT_DIR/"
|
||||||
|
|
||||||
|
# Запуск контейнера:
|
||||||
|
# - вход: только .md файл (read-only)
|
||||||
|
# - выход: директория для .tex и .pdf
|
||||||
|
# - user: текущий пользователь (не root)
|
||||||
docker run --rm \
|
docker run --rm \
|
||||||
-e UV_CACHE_DIR=/tmp/uv-cache \
|
-v "$INPUT_ABS:/input/$INPUT_BASE:ro" \
|
||||||
-v "$(pwd):/app" \
|
-v "$OUTPUT_ABS:/output" \
|
||||||
"$IMAGE" "$@"
|
--user "$(id -u):$(id -g)" \
|
||||||
|
"$IMAGE" "/input/$INPUT_BASE" -o /output
|
||||||
|
|||||||
+33
-17
@@ -1,6 +1,8 @@
|
|||||||
import argparse
|
import argparse
|
||||||
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
import tempfile
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from md2gost.parser import Parser
|
from md2gost.parser import Parser
|
||||||
@@ -44,27 +46,41 @@ def main():
|
|||||||
renderer = Renderer(parser.tokens, parser.front_matter)
|
renderer = Renderer(parser.tokens, parser.front_matter)
|
||||||
latex = renderer.render()
|
latex = renderer.render()
|
||||||
|
|
||||||
tex_path = args.output / args.input.with_suffix(".tex").name
|
# Собираем во временную директорию, копируем только .tex и .pdf
|
||||||
tex_path.write_text(latex, encoding="utf-8")
|
with tempfile.TemporaryDirectory(prefix="md2gost-") as cache:
|
||||||
print(f"→ {tex_path}")
|
cache_dir = Path(cache)
|
||||||
|
|
||||||
if not args.no_pdf:
|
# Генерируем .tex в кэш
|
||||||
pdf_path = args.output / args.input.with_suffix(".pdf").name
|
tex_name = args.input.with_suffix(".tex").name
|
||||||
log_path = args.output / "report.log"
|
tex_path = cache_dir / tex_name
|
||||||
|
tex_path.write_text(latex, encoding="utf-8")
|
||||||
|
|
||||||
if not run_xelatex(tex_path, args.output):
|
# Копируем .tex в выходную директорию
|
||||||
print("ошибка: xelatex не собрал PDF", file=sys.stderr)
|
final_tex = args.output / tex_name
|
||||||
if log_path.exists():
|
shutil.copy2(tex_path, final_tex)
|
||||||
for line in log_path.read_text(errors="replace").splitlines():
|
print(f"→ {final_tex}")
|
||||||
if line.startswith("!"):
|
|
||||||
print(f" {line}", file=sys.stderr)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
if not run_xelatex(tex_path, args.output):
|
if not args.no_pdf:
|
||||||
print("ошибка: xelatex (2-й проход) не собрал PDF", file=sys.stderr)
|
# Компилируем PDF в кэше (два прохода для TOC)
|
||||||
sys.exit(1)
|
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__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Reference in New Issue
Block a user