mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-11-06 05:17:15 +00:00
fix(skills): document env setup never deletes a working env another agent published
The sandbox fallback in the document skills (microsoft-office, pdf,
excel-author, pptx-author and the two OCR blocks) read the published
venv only after its own import check failed. When another agent
published a working venv in between, this caller took it for a broken
build, swapped in its own and deleted the working one, so that agent's
running process could lose lazily imported files. A published build is
now replaced, and deleted, only while it still fails the import check;
otherwise the late caller discards its own stage. Generated skill pages
regenerated.
Introduced by dc82bec3bd (#146111, @teknium1, 2026-10-28); not a
regression, the skills had no sandbox venv before it.
This commit is contained in:
1 parent
b544b82db1
commit
a53acf8d3a
10 files changed
+87
-30
No files matched your search
@@ -40,14 +40,15 @@ else
|
||||
PY="$HOME/.cache/hermes-docs-venv/bin/python"
|
||||
"$PY" -c "import docx, openpyxl, pptx, reportlab, pypdf, pdfplumber, pypdfium2" 2>/dev/null || (
|
||||
# Safe to run from several agents at once: each caller builds a private venv,
|
||||
# a symlink publishes the first finished one atomically, the others delete theirs.
|
||||
# a symlink publishes the first finished one atomically, the others delete theirs;
|
||||
# a published build is replaced and deleted only while it still fails to import.
|
||||
mkdir -p "$HOME/.cache" && cd "$HOME/.cache" && stage="$(mktemp -d hermes-docs-venv.XXXXXX)" || exit 1
|
||||
old="$(readlink hermes-docs-venv)" # an earlier build that no longer imports, if any
|
||||
old="$(readlink hermes-docs-venv)" # the published build, if any (another agent may have just published it)
|
||||
python3 -m venv "$stage" &&
|
||||
"$stage/bin/python" -m pip install -q --disable-pip-version-check python-docx==1.2.0 openpyxl==3.1.5 python-pptx==1.0.2 reportlab==5.1.0 pypdf==6.21.2 pdfplumber==0.11.10 pypdfium2==5.14.0 &&
|
||||
"$stage/bin/python" -c "import docx, openpyxl, pptx, reportlab, pypdf, pdfplumber, pypdfium2" &&
|
||||
if [ -z "$old" ]; then python3 -c 'import os, sys; os.symlink(*sys.argv[1:])' "$stage" hermes-docs-venv
|
||||
elif [ "$(readlink hermes-docs-venv)" = "$old" ]; then
|
||||
elif [ "$(readlink hermes-docs-venv)" = "$old" ] && ! "$old/bin/python" -c "import docx, openpyxl, pptx, reportlab, pypdf, pdfplumber, pypdfium2" 2>/dev/null; then
|
||||
ln -s "$stage" "$stage.lnk" && python3 -c 'import os, sys; os.replace(*sys.argv[1:])' "$stage.lnk" hermes-docs-venv &&
|
||||
case "$old" in hermes-docs-venv.??????) rm -rf "$old" ;; esac
|
||||
fi 2>/dev/null
|
||||
|
||||
@@ -41,14 +41,15 @@ else
|
||||
PY="$HOME/.cache/hermes-docs-venv/bin/python"
|
||||
"$PY" -c "import docx, openpyxl, pptx, reportlab, pypdf, pdfplumber, pypdfium2" 2>/dev/null || (
|
||||
# Safe to run from several agents at once: each caller builds a private venv,
|
||||
# a symlink publishes the first finished one atomically, the others delete theirs.
|
||||
# a symlink publishes the first finished one atomically, the others delete theirs;
|
||||
# a published build is replaced and deleted only while it still fails to import.
|
||||
mkdir -p "$HOME/.cache" && cd "$HOME/.cache" && stage="$(mktemp -d hermes-docs-venv.XXXXXX)" || exit 1
|
||||
old="$(readlink hermes-docs-venv)" # an earlier build that no longer imports, if any
|
||||
old="$(readlink hermes-docs-venv)" # the published build, if any (another agent may have just published it)
|
||||
python3 -m venv "$stage" &&
|
||||
"$stage/bin/python" -m pip install -q --disable-pip-version-check python-docx==1.2.0 openpyxl==3.1.5 python-pptx==1.0.2 reportlab==5.1.0 pypdf==6.21.2 pdfplumber==0.11.10 pypdfium2==5.14.0 &&
|
||||
"$stage/bin/python" -c "import docx, openpyxl, pptx, reportlab, pypdf, pdfplumber, pypdfium2" &&
|
||||
if [ -z "$old" ]; then python3 -c 'import os, sys; os.symlink(*sys.argv[1:])' "$stage" hermes-docs-venv
|
||||
elif [ "$(readlink hermes-docs-venv)" = "$old" ]; then
|
||||
elif [ "$(readlink hermes-docs-venv)" = "$old" ] && ! "$old/bin/python" -c "import docx, openpyxl, pptx, reportlab, pypdf, pdfplumber, pypdfium2" 2>/dev/null; then
|
||||
ln -s "$stage" "$stage.lnk" && python3 -c 'import os, sys; os.replace(*sys.argv[1:])' "$stage.lnk" hermes-docs-venv &&
|
||||
case "$old" in hermes-docs-venv.??????) rm -rf "$old" ;; esac
|
||||
fi 2>/dev/null
|
||||
|
||||
@@ -57,14 +57,15 @@ Deeper notes: `references/revisions-and-comments.md` (Word review markup) and
|
||||
PY="$HOME/.cache/hermes-docs-venv/bin/python"
|
||||
"$PY" -c "import docx, openpyxl, pptx, reportlab, pypdf, pdfplumber, pypdfium2" 2>/dev/null || (
|
||||
# Safe to run from several agents at once: each caller builds a private venv,
|
||||
# a symlink publishes the first finished one atomically, the others delete theirs.
|
||||
# a symlink publishes the first finished one atomically, the others delete theirs;
|
||||
# a published build is replaced and deleted only while it still fails to import.
|
||||
mkdir -p "$HOME/.cache" && cd "$HOME/.cache" && stage="$(mktemp -d hermes-docs-venv.XXXXXX)" || exit 1
|
||||
old="$(readlink hermes-docs-venv)" # an earlier build that no longer imports, if any
|
||||
old="$(readlink hermes-docs-venv)" # the published build, if any (another agent may have just published it)
|
||||
python3 -m venv "$stage" &&
|
||||
"$stage/bin/python" -m pip install -q --disable-pip-version-check python-docx==1.2.0 openpyxl==3.1.5 python-pptx==1.0.2 reportlab==5.1.0 pypdf==6.21.2 pdfplumber==0.11.10 pypdfium2==5.14.0 &&
|
||||
"$stage/bin/python" -c "import docx, openpyxl, pptx, reportlab, pypdf, pdfplumber, pypdfium2" &&
|
||||
if [ -z "$old" ]; then python3 -c 'import os, sys; os.symlink(*sys.argv[1:])' "$stage" hermes-docs-venv
|
||||
elif [ "$(readlink hermes-docs-venv)" = "$old" ]; then
|
||||
elif [ "$(readlink hermes-docs-venv)" = "$old" ] && ! "$old/bin/python" -c "import docx, openpyxl, pptx, reportlab, pypdf, pdfplumber, pypdfium2" 2>/dev/null; then
|
||||
ln -s "$stage" "$stage.lnk" && python3 -c 'import os, sys; os.replace(*sys.argv[1:])' "$stage.lnk" hermes-docs-venv &&
|
||||
case "$old" in hermes-docs-venv.??????) rm -rf "$old" ;; esac
|
||||
fi 2>/dev/null
|
||||
|
||||
@@ -48,14 +48,15 @@ Create PDFs from structured specs, build and fill AcroForm forms (with layout li
|
||||
PY="$HOME/.cache/hermes-docs-venv/bin/python"
|
||||
"$PY" -c "import docx, openpyxl, pptx, reportlab, pypdf, pdfplumber, pypdfium2" 2>/dev/null || (
|
||||
# Safe to run from several agents at once: each caller builds a private venv,
|
||||
# a symlink publishes the first finished one atomically, the others delete theirs.
|
||||
# a symlink publishes the first finished one atomically, the others delete theirs;
|
||||
# a published build is replaced and deleted only while it still fails to import.
|
||||
mkdir -p "$HOME/.cache" && cd "$HOME/.cache" && stage="$(mktemp -d hermes-docs-venv.XXXXXX)" || exit 1
|
||||
old="$(readlink hermes-docs-venv)" # an earlier build that no longer imports, if any
|
||||
old="$(readlink hermes-docs-venv)" # the published build, if any (another agent may have just published it)
|
||||
python3 -m venv "$stage" &&
|
||||
"$stage/bin/python" -m pip install -q --disable-pip-version-check python-docx==1.2.0 openpyxl==3.1.5 python-pptx==1.0.2 reportlab==5.1.0 pypdf==6.21.2 pdfplumber==0.11.10 pypdfium2==5.14.0 &&
|
||||
"$stage/bin/python" -c "import docx, openpyxl, pptx, reportlab, pypdf, pdfplumber, pypdfium2" &&
|
||||
if [ -z "$old" ]; then python3 -c 'import os, sys; os.symlink(*sys.argv[1:])' "$stage" hermes-docs-venv
|
||||
elif [ "$(readlink hermes-docs-venv)" = "$old" ]; then
|
||||
elif [ "$(readlink hermes-docs-venv)" = "$old" ] && ! "$old/bin/python" -c "import docx, openpyxl, pptx, reportlab, pypdf, pdfplumber, pypdfium2" 2>/dev/null; then
|
||||
ln -s "$stage" "$stage.lnk" && python3 -c 'import os, sys; os.replace(*sys.argv[1:])' "$stage.lnk" hermes-docs-venv &&
|
||||
case "$old" in hermes-docs-venv.??????) rm -rf "$old" ;; esac
|
||||
fi 2>/dev/null
|
||||
|
||||
@@ -65,12 +65,12 @@ else
|
||||
"$PY" -c "import pymupdf, pymupdf4llm" 2>/dev/null || (
|
||||
# concurrency-safe build: see the documents block in SKILL.md
|
||||
mkdir -p "$HOME/.cache" && cd "$HOME/.cache" && stage="$(mktemp -d hermes-pdf-pymupdf-venv.XXXXXX)" || exit 1
|
||||
old="$(readlink hermes-pdf-pymupdf-venv)" # an earlier build that no longer imports, if any
|
||||
old="$(readlink hermes-pdf-pymupdf-venv)" # the published build, if any (another agent may have just published it)
|
||||
python3 -m venv "$stage" &&
|
||||
"$stage/bin/python" -m pip install -q --disable-pip-version-check 'pymupdf>=1.26,<3' 'pymupdf4llm>=0.2,<3' &&
|
||||
"$stage/bin/python" -c "import pymupdf, pymupdf4llm" &&
|
||||
if [ -z "$old" ]; then python3 -c 'import os, sys; os.symlink(*sys.argv[1:])' "$stage" hermes-pdf-pymupdf-venv
|
||||
elif [ "$(readlink hermes-pdf-pymupdf-venv)" = "$old" ]; then
|
||||
elif [ "$(readlink hermes-pdf-pymupdf-venv)" = "$old" ] && ! "$old/bin/python" -c "import pymupdf, pymupdf4llm" 2>/dev/null; then
|
||||
ln -s "$stage" "$stage.lnk" && python3 -c 'import os, sys; os.replace(*sys.argv[1:])' "$stage.lnk" hermes-pdf-pymupdf-venv &&
|
||||
case "$old" in hermes-pdf-pymupdf-venv.??????) rm -rf "$old" ;; esac
|
||||
fi 2>/dev/null
|
||||
@@ -116,12 +116,12 @@ else
|
||||
"$PY" -c "import marker" 2>/dev/null || (
|
||||
# concurrency-safe build: see the documents block in SKILL.md
|
||||
mkdir -p "$HOME/.cache" && cd "$HOME/.cache" && stage="$(mktemp -d hermes-pdf-marker-venv.XXXXXX)" || exit 1
|
||||
old="$(readlink hermes-pdf-marker-venv)" # an earlier build that no longer imports, if any
|
||||
old="$(readlink hermes-pdf-marker-venv)" # the published build, if any (another agent may have just published it)
|
||||
python3 -m venv "$stage" &&
|
||||
"$stage/bin/python" -m pip install -q --disable-pip-version-check 'marker-pdf>=1.8,<3' &&
|
||||
"$stage/bin/python" -c "import marker" &&
|
||||
if [ -z "$old" ]; then python3 -c 'import os, sys; os.symlink(*sys.argv[1:])' "$stage" hermes-pdf-marker-venv
|
||||
elif [ "$(readlink hermes-pdf-marker-venv)" = "$old" ]; then
|
||||
elif [ "$(readlink hermes-pdf-marker-venv)" = "$old" ] && ! "$old/bin/python" -c "import marker" 2>/dev/null; then
|
||||
ln -s "$stage" "$stage.lnk" && python3 -c 'import os, sys; os.replace(*sys.argv[1:])' "$stage.lnk" hermes-pdf-marker-venv &&
|
||||
case "$old" in hermes-pdf-marker-venv.??????) rm -rf "$old" ;; esac
|
||||
fi 2>/dev/null
|
||||
|
||||
@@ -56,9 +56,16 @@ def _script(path, body):
|
||||
path.chmod(0o755)
|
||||
|
||||
|
||||
def _sandbox(tmp_path, *, hermes=None):
|
||||
def _venv(directory, healthy):
|
||||
"""A stand-in venv whose interpreter passes (or fails) every import check and pip install."""
|
||||
(directory / "bin").mkdir(parents=True)
|
||||
_script(directory / "bin" / "python", "exit 0\n" if healthy else "exit 1\n")
|
||||
|
||||
|
||||
def _sandbox(tmp_path, *, hermes=None, before_stage=""):
|
||||
"""A HOME and a PATH for one block: ``python`` lacks every library and ``python3 -m venv``
|
||||
makes a stub venv."""
|
||||
makes a stub venv. ``before_stage`` runs inside the block's ``$HOME/.cache`` right before it
|
||||
creates its private stage, i.e. after its own import check failed."""
|
||||
system_path = _system_path()
|
||||
if shutil.which("hermes", path=system_path):
|
||||
pytest.skip("a hermes on the system PATH would take the PM branch")
|
||||
@@ -70,6 +77,7 @@ def _sandbox(tmp_path, *, hermes=None):
|
||||
if [ "$1 $2" = "-m venv" ]; then mkdir -p "$3/bin" && printf '#!/bin/sh\\nexit 0\\n' > "$3/bin/python" && chmod +x "$3/bin/python"; exit; fi
|
||||
exec {shlex.quote(sys.executable)} "$@"
|
||||
"""))
|
||||
_script(stubs / "mktemp", f"{before_stage}\nexec {shlex.quote(shutil.which('mktemp'))} \"$@\"\n")
|
||||
if hermes is not None:
|
||||
_script(stubs / "hermes", f"echo {hermes}\n")
|
||||
return home, {"HOME": str(home), "PATH": f"{stubs}{os.pathsep}{system_path}", "LC_ALL": "C"}
|
||||
@@ -89,3 +97,44 @@ def test_block_exports_py_for_the_next_terminal_call(tmp_path, document, venv):
|
||||
home, env = _sandbox(tmp_path, hermes="/side-env/bin/python")
|
||||
exported = _run(_setup_block(document, venv) + "export -p\n", env, home)
|
||||
assert "PY=" in exported and "/side-env/bin/python" in exported, exported
|
||||
|
||||
|
||||
@pytest.mark.parametrize("document,venv", BLOCKS)
|
||||
def test_late_builder_keeps_the_env_another_caller_just_published(tmp_path, document, venv):
|
||||
# Another caller publishes a working build between this caller's failed import check and
|
||||
# its stage: this caller must not replace it and delete it under that caller.
|
||||
published = f"{venv}.PUBLSH"
|
||||
publish = (f'[ -e {published} ] || {{ mkdir -p {published}/bin && printf "#!/bin/sh\\nexit 0\\n" '
|
||||
f'> {published}/bin/python && chmod +x {published}/bin/python && ln -s {published} {venv}; }}')
|
||||
home, env = _sandbox(tmp_path, before_stage=publish)
|
||||
_run(_setup_block(document, venv), env, home)
|
||||
cache = home / ".cache"
|
||||
assert (cache / published / "bin" / "python").is_file()
|
||||
assert os.readlink(cache / venv) == published
|
||||
assert sorted(p.name for p in cache.iterdir()) == [venv, published]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("document,venv", BLOCKS)
|
||||
def test_broken_published_env_is_replaced_and_removed(tmp_path, document, venv):
|
||||
home, env = _sandbox(tmp_path)
|
||||
cache = home / ".cache"
|
||||
_venv(cache / f"{venv}.BROKEN", healthy=False)
|
||||
(cache / venv).symlink_to(f"{venv}.BROKEN")
|
||||
_run(_setup_block(document, venv), env, home)
|
||||
fresh = os.readlink(cache / venv)
|
||||
assert fresh.startswith(f"{venv}.") and fresh != f"{venv}.BROKEN"
|
||||
assert sorted(p.name for p in cache.iterdir()) == sorted([venv, fresh])
|
||||
|
||||
|
||||
def test_repair_still_works_when_the_test_tools_live_under_a_spaced_path(tmp_path, monkeypatch):
|
||||
# The stubs exec the real interpreter and mktemp; a checkout or venv under a spaced path
|
||||
# must not split those commands into words (the blocks themselves are not involved).
|
||||
spaced = tmp_path / "test tools"
|
||||
spaced.mkdir()
|
||||
(spaced / "python").symlink_to(sys.executable)
|
||||
(spaced / "mktemp").symlink_to(shutil.which("mktemp"))
|
||||
real_which = shutil.which
|
||||
monkeypatch.setattr(sys, "executable", str(spaced / "python"))
|
||||
monkeypatch.setattr(shutil, "which",
|
||||
lambda cmd, *a, **k: str(spaced / "mktemp") if cmd == "mktemp" else real_which(cmd, *a, **k))
|
||||
test_broken_published_env_is_replaced_and_removed(tmp_path, *BLOCKS[1])
|
||||
+4
-3
@@ -74,14 +74,15 @@ Deeper notes: `references/revisions-and-comments.md` (Word review markup) and
|
||||
PY="$HOME/.cache/hermes-docs-venv/bin/python"
|
||||
"$PY" -c "import docx, openpyxl, pptx, reportlab, pypdf, pdfplumber, pypdfium2" 2>/dev/null || (
|
||||
# Safe to run from several agents at once: each caller builds a private venv,
|
||||
# a symlink publishes the first finished one atomically, the others delete theirs.
|
||||
# a symlink publishes the first finished one atomically, the others delete theirs;
|
||||
# a published build is replaced and deleted only while it still fails to import.
|
||||
mkdir -p "$HOME/.cache" && cd "$HOME/.cache" && stage="$(mktemp -d hermes-docs-venv.XXXXXX)" || exit 1
|
||||
old="$(readlink hermes-docs-venv)" # an earlier build that no longer imports, if any
|
||||
old="$(readlink hermes-docs-venv)" # the published build, if any (another agent may have just published it)
|
||||
python3 -m venv "$stage" &&
|
||||
"$stage/bin/python" -m pip install -q --disable-pip-version-check python-docx==1.2.0 openpyxl==3.1.5 python-pptx==1.0.2 reportlab==5.1.0 pypdf==6.21.2 pdfplumber==0.11.10 pypdfium2==5.14.0 &&
|
||||
"$stage/bin/python" -c "import docx, openpyxl, pptx, reportlab, pypdf, pdfplumber, pypdfium2" &&
|
||||
if [ -z "$old" ]; then python3 -c 'import os, sys; os.symlink(*sys.argv[1:])' "$stage" hermes-docs-venv
|
||||
elif [ "$(readlink hermes-docs-venv)" = "$old" ]; then
|
||||
elif [ "$(readlink hermes-docs-venv)" = "$old" ] && ! "$old/bin/python" -c "import docx, openpyxl, pptx, reportlab, pypdf, pdfplumber, pypdfium2" 2>/dev/null; then
|
||||
ln -s "$stage" "$stage.lnk" && python3 -c 'import os, sys; os.replace(*sys.argv[1:])' "$stage.lnk" hermes-docs-venv &&
|
||||
case "$old" in hermes-docs-venv.??????) rm -rf "$old" ;; esac
|
||||
fi 2>/dev/null
|
||||
|
||||
@@ -65,14 +65,15 @@ Create PDFs from structured specs, build and fill AcroForm forms (with layout li
|
||||
PY="$HOME/.cache/hermes-docs-venv/bin/python"
|
||||
"$PY" -c "import docx, openpyxl, pptx, reportlab, pypdf, pdfplumber, pypdfium2" 2>/dev/null || (
|
||||
# Safe to run from several agents at once: each caller builds a private venv,
|
||||
# a symlink publishes the first finished one atomically, the others delete theirs.
|
||||
# a symlink publishes the first finished one atomically, the others delete theirs;
|
||||
# a published build is replaced and deleted only while it still fails to import.
|
||||
mkdir -p "$HOME/.cache" && cd "$HOME/.cache" && stage="$(mktemp -d hermes-docs-venv.XXXXXX)" || exit 1
|
||||
old="$(readlink hermes-docs-venv)" # an earlier build that no longer imports, if any
|
||||
old="$(readlink hermes-docs-venv)" # the published build, if any (another agent may have just published it)
|
||||
python3 -m venv "$stage" &&
|
||||
"$stage/bin/python" -m pip install -q --disable-pip-version-check python-docx==1.2.0 openpyxl==3.1.5 python-pptx==1.0.2 reportlab==5.1.0 pypdf==6.21.2 pdfplumber==0.11.10 pypdfium2==5.14.0 &&
|
||||
"$stage/bin/python" -c "import docx, openpyxl, pptx, reportlab, pypdf, pdfplumber, pypdfium2" &&
|
||||
if [ -z "$old" ]; then python3 -c 'import os, sys; os.symlink(*sys.argv[1:])' "$stage" hermes-docs-venv
|
||||
elif [ "$(readlink hermes-docs-venv)" = "$old" ]; then
|
||||
elif [ "$(readlink hermes-docs-venv)" = "$old" ] && ! "$old/bin/python" -c "import docx, openpyxl, pptx, reportlab, pypdf, pdfplumber, pypdfium2" 2>/dev/null; then
|
||||
ln -s "$stage" "$stage.lnk" && python3 -c 'import os, sys; os.replace(*sys.argv[1:])' "$stage.lnk" hermes-docs-venv &&
|
||||
case "$old" in hermes-docs-venv.??????) rm -rf "$old" ;; esac
|
||||
fi 2>/dev/null
|
||||
|
||||
@@ -58,14 +58,15 @@ else
|
||||
PY="$HOME/.cache/hermes-docs-venv/bin/python"
|
||||
"$PY" -c "import docx, openpyxl, pptx, reportlab, pypdf, pdfplumber, pypdfium2" 2>/dev/null || (
|
||||
# Safe to run from several agents at once: each caller builds a private venv,
|
||||
# a symlink publishes the first finished one atomically, the others delete theirs.
|
||||
# a symlink publishes the first finished one atomically, the others delete theirs;
|
||||
# a published build is replaced and deleted only while it still fails to import.
|
||||
mkdir -p "$HOME/.cache" && cd "$HOME/.cache" && stage="$(mktemp -d hermes-docs-venv.XXXXXX)" || exit 1
|
||||
old="$(readlink hermes-docs-venv)" # an earlier build that no longer imports, if any
|
||||
old="$(readlink hermes-docs-venv)" # the published build, if any (another agent may have just published it)
|
||||
python3 -m venv "$stage" &&
|
||||
"$stage/bin/python" -m pip install -q --disable-pip-version-check python-docx==1.2.0 openpyxl==3.1.5 python-pptx==1.0.2 reportlab==5.1.0 pypdf==6.21.2 pdfplumber==0.11.10 pypdfium2==5.14.0 &&
|
||||
"$stage/bin/python" -c "import docx, openpyxl, pptx, reportlab, pypdf, pdfplumber, pypdfium2" &&
|
||||
if [ -z "$old" ]; then python3 -c 'import os, sys; os.symlink(*sys.argv[1:])' "$stage" hermes-docs-venv
|
||||
elif [ "$(readlink hermes-docs-venv)" = "$old" ]; then
|
||||
elif [ "$(readlink hermes-docs-venv)" = "$old" ] && ! "$old/bin/python" -c "import docx, openpyxl, pptx, reportlab, pypdf, pdfplumber, pypdfium2" 2>/dev/null; then
|
||||
ln -s "$stage" "$stage.lnk" && python3 -c 'import os, sys; os.replace(*sys.argv[1:])' "$stage.lnk" hermes-docs-venv &&
|
||||
case "$old" in hermes-docs-venv.??????) rm -rf "$old" ;; esac
|
||||
fi 2>/dev/null
|
||||
|
||||
@@ -59,14 +59,15 @@ else
|
||||
PY="$HOME/.cache/hermes-docs-venv/bin/python"
|
||||
"$PY" -c "import docx, openpyxl, pptx, reportlab, pypdf, pdfplumber, pypdfium2" 2>/dev/null || (
|
||||
# Safe to run from several agents at once: each caller builds a private venv,
|
||||
# a symlink publishes the first finished one atomically, the others delete theirs.
|
||||
# a symlink publishes the first finished one atomically, the others delete theirs;
|
||||
# a published build is replaced and deleted only while it still fails to import.
|
||||
mkdir -p "$HOME/.cache" && cd "$HOME/.cache" && stage="$(mktemp -d hermes-docs-venv.XXXXXX)" || exit 1
|
||||
old="$(readlink hermes-docs-venv)" # an earlier build that no longer imports, if any
|
||||
old="$(readlink hermes-docs-venv)" # the published build, if any (another agent may have just published it)
|
||||
python3 -m venv "$stage" &&
|
||||
"$stage/bin/python" -m pip install -q --disable-pip-version-check python-docx==1.2.0 openpyxl==3.1.5 python-pptx==1.0.2 reportlab==5.1.0 pypdf==6.21.2 pdfplumber==0.11.10 pypdfium2==5.14.0 &&
|
||||
"$stage/bin/python" -c "import docx, openpyxl, pptx, reportlab, pypdf, pdfplumber, pypdfium2" &&
|
||||
if [ -z "$old" ]; then python3 -c 'import os, sys; os.symlink(*sys.argv[1:])' "$stage" hermes-docs-venv
|
||||
elif [ "$(readlink hermes-docs-venv)" = "$old" ]; then
|
||||
elif [ "$(readlink hermes-docs-venv)" = "$old" ] && ! "$old/bin/python" -c "import docx, openpyxl, pptx, reportlab, pypdf, pdfplumber, pypdfium2" 2>/dev/null; then
|
||||
ln -s "$stage" "$stage.lnk" && python3 -c 'import os, sys; os.replace(*sys.argv[1:])' "$stage.lnk" hermes-docs-venv &&
|
||||
case "$old" in hermes-docs-venv.??????) rm -rf "$old" ;; esac
|
||||
fi 2>/dev/null
|
||||
|
||||
Reference in new issue
Block a user