From edf0b98abd0d6330516dabca4c640212f958c0d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Shixiang=20Wang=20=28=E7=8E=8B=E8=AF=97=E7=BF=94=29?= Date: Sun, 11 Oct 2026 13:25:01 +0800 Subject: [PATCH 1/3] fix(ci): install Julia + QuartoNotebookRunner in ci-benchmark MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cold benchmark run 38109155362 (force_full_rebuild=true) failed at "Render website with Babelquarto" on [221/270] Julia/Heatmap.qmd: with _freeze deleted, Julia qmds re-execute and the julia-engine tried to bootstrap its QuartoNotebookRunner server at render time, dying with "ERROR: Ensuring an updated julia server environment failed" (after a zstd library-version mismatch inside Pkg's registry setup). The benchmark workflow had no Julia setup at all — its only Julia reference was the worker pre-render skip. Mirror the proven setup from quarto-publish-update-cache.yml: julia-actions/setup-julia, the pinned QNR env (QUARTO_JULIA_PROJECT skips Quarto's network-dependent ensureQuartoNotebookRunnerEnvironment at render time), and the Julia/ tutorial project instantiate. Two deliberate deviations from the update-cache workflow: - cache/restore instead of cache: the benchmark must never SAVE caches (its header says so — saving would pollute the shared pool); restore still lets warm benchmark runs reuse the shared ~/.julia cache. - Julia setup is unconditional (no need_julia gate): the benchmark has no changed-file detection step to feed such a gate, and a cold run always re-executes Julia qmds. Also clean up the phase marker: Julia setup time lands between python_setup and pre_render marks, so "Pre-render non-frozen QMDs" in the report absorbs it — acceptable for a manual-diagnostic workflow, not worth extra clock entries. Co-Authored-By: Claude Code --- .github/workflows/ci-benchmark.yml | 72 ++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/.github/workflows/ci-benchmark.yml b/.github/workflows/ci-benchmark.yml index 374ebbd69..103f5bb67 100644 --- a/.github/workflows/ci-benchmark.yml +++ b/.github/workflows/ci-benchmark.yml @@ -186,6 +186,78 @@ jobs: - name: Mark phase — python setup done run: echo "python_setup=$(date +%s)" >> /tmp/benchmark-clock + # ── Julia Setup ────────────────────────────────────────── + # Cold builds (force_full_rebuild) delete _freeze, so Julia/Heatmap.qmd + # and friends re-execute during the babelquarto render — benchmark run + # 38109155362 died there with "Ensuring an updated julia server + # environment failed" because no Julia was installed. Mirror the + # setup from quarto-publish-update-cache.yml: Julia + a pinned + # QuartoNotebookRunner environment (QUARTO_JULIA_PROJECT skips Quarto's + # ensureQuartoNotebookRunnerEnvironment, which needs network+Pkg.update + # at render time) + the tutorial project env. + - name: Set up Julia + uses: julia-actions/setup-julia@v3 + with: + version: '1' + + - name: Cache Julia packages + uses: actions/cache/restore@v6 + with: + path: ~/.julia + key: julia-packages-${{ runner.os }}-${{ hashFiles('Julia/Project.toml') }} + restore-keys: | + julia-packages-${{ runner.os }}- + + - name: Install Julia packages + run: | + # Find the QuartoNotebookRunner version required by this Quarto installation. + # Quarto bundles its own julia-engine Project.toml that pins an exact QNR version. + JULIA_ENGINE_PROJ=$(find /opt/quarto -name "Project.toml" -path "*/julia-engine/*" 2>/dev/null | head -1) + QNR_VERSION="" + if [ -n "$JULIA_ENGINE_PROJ" ]; then + QNR_VERSION=$(grep -oP 'QuartoNotebookRunner = "=([^"]+)"' "$JULIA_ENGINE_PROJ" \ + | grep -oP '[0-9]+\.[0-9]+\.[0-9]+' || true) + echo "Quarto requires QuartoNotebookRunner=${QNR_VERSION}" + fi + + # Create a dedicated QNR environment. Setting QUARTO_JULIA_PROJECT to this + # path tells Quarto to skip ensureQuartoNotebookRunnerEnvironment (which calls + # Pkg.update() and needs network at render time) and simply verify QNR loads. + QNR_ENV="${HOME}/.julia/environments/quarto-julia-env" + mkdir -p "${QNR_ENV}" + + if [ -n "$QNR_VERSION" ]; then + julia --project="${QNR_ENV}" -e " + using Pkg + Pkg.add(name=\"QuartoNotebookRunner\", version=\"${QNR_VERSION}\") + Pkg.instantiate() + using QuartoNotebookRunner + println(\"QuartoNotebookRunner \$(pkgversion(QuartoNotebookRunner)) ready\") + " + else + julia --project="${QNR_ENV}" -e ' + using Pkg + Pkg.add("QuartoNotebookRunner") + Pkg.instantiate() + using QuartoNotebookRunner + println("QuartoNotebookRunner $(pkgversion(QuartoNotebookRunner)) ready") + ' + fi + + # Export so the render step can pick it up. + echo "QUARTO_JULIA_PROJECT=${QNR_ENV}" >> "$GITHUB_ENV" + + # Pre-instantiate the tutorial project environment (Julia/Project.toml). + # QNR workers use JULIA_PROJECT=@. and cd into the notebook directory, + # so they automatically pick up Julia/Project.toml for CairoMakie etc. + julia --project=Julia -e ' + using Pkg + Pkg.resolve() + Pkg.instantiate() + using CairoMakie, DataFrames, Statistics + println("Tutorial packages ready") + ' + # ── Render pipeline (mirrors quarto-publish-update-cache.yml) ── - name: Pre-render all non-frozen QMD files env: From 66874bbe000fca6094a813dccc1ca651a1461fdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Shixiang=20Wang=20=28=E7=8E=8B=E8=AF=97=E7=BF=94=29?= Date: Sun, 11 Oct 2026 15:09:26 +0800 Subject: [PATCH 2/3] fix(ci): pre-render Julia notebooks in ci-benchmark before babelquarto Second validation run 38114723635 proved the Julia setup works (setup/ cache/install all green, zstd bootstrap error gone, QUARTO_JULIA_PROJECT set) but babelquarto still failed at [221/270] Julia/Heatmap.qmd with "ERROR: SyntaxError: Unexpected end of JSON input" from julia-engine's JSON handshake with the QuartoNotebookRunner server (writeJuliaCommand, julia-engine.js:1202). Structural difference vs. the working quarto-publish-update-cache.yml: the benchmark had no dedicated "Pre-render Julia notebooks" step, so Julia qmds executed inline inside babelquarto's temp-dir render. Port the proven pre-render step from update-cache: render Julia/*.qmd before the parallel pre-render phase, with QUARTO_JULIA_PROJECT + TMPDIR set and a julia_server_log tail on failure. This freezes Julia outputs into the workspace _freeze so babelquarto only pandoc-renders them. Co-Authored-By: Claude Code --- .github/workflows/ci-benchmark.yml | 32 ++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/.github/workflows/ci-benchmark.yml b/.github/workflows/ci-benchmark.yml index 103f5bb67..b55c0e068 100644 --- a/.github/workflows/ci-benchmark.yml +++ b/.github/workflows/ci-benchmark.yml @@ -258,6 +258,38 @@ jobs: println("Tutorial packages ready") ' + # Pre-render Julia notebooks to avoid OOM during the R-heavy main render. + # Also REQUIRED for correctness: without this step babelquarto executes + # Julia qmds inline in its temp dir and the julia-engine JSON handshake + # fails ("SyntaxError: Unexpected end of JSON input" — CI run + # 38114723635). Pre-rendering freezes Julia/*.qmd into the workspace + # _freeze so babelquarto never executes them. + - name: Pre-render Julia notebooks + env: + QUARTO_JULIA_PROJECT: ${{ env.QUARTO_JULIA_PROJECT }} + TMPDIR: ${{ env.TMP_DIR }} + TMP: ${{ env.TMP_DIR }} + TEMP: ${{ env.TMP_DIR }} + run: | + echo "Pre-rendering Julia tutorials..." + failed=0 + for qmd in Julia/*.qmd; do + echo " → $qmd" + if quarto render "$qmd" 2>&1; then + echo " ✓ OK: $qmd" + else + echo " ✗ Failed: $qmd" + failed=1 + fi + done + julia_log="${HOME}/.cache/quarto/julia/julia_server_log.txt" + if [ -f "$julia_log" ]; then + echo "=== QNR server log ===" + tail -50 "$julia_log" + echo "======================" + fi + [ $failed -eq 0 ] || exit 1 + # ── Render pipeline (mirrors quarto-publish-update-cache.yml) ── - name: Pre-render all non-frozen QMD files env: From 8f7d03c804814d794278da4dc1dcec36f7190187 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Shixiang=20Wang=20=28=E7=8E=8B=E8=AF=97=E7=BF=94=29?= Date: Sun, 11 Oct 2026 15:37:02 +0800 Subject: [PATCH 3/3] fix(ci): create TMP_DIR before Julia pre-render in ci-benchmark Third validation run 38120426338 failed in the new pre-render step itself: every Julia/Heatmap.qmd render died in under a second with "NotFound: No such file or directory (os error 2): tmpdir" from quarto's initSessionTempDir (makeTempDirSync). The step sets TMPDIR/TMP/TEMP=/mnt/TMP, but in ci-benchmark that directory was only created later (in the R pre-render step's own mkdir); in quarto-publish-update-cache.yml a dedicated "Create temporary directory" step runs before the render pipeline. Move that mkdir/chown step ahead of "Pre-render Julia notebooks". Co-Authored-By: Claude Code --- .github/workflows/ci-benchmark.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/ci-benchmark.yml b/.github/workflows/ci-benchmark.yml index b55c0e068..92ebdafa8 100644 --- a/.github/workflows/ci-benchmark.yml +++ b/.github/workflows/ci-benchmark.yml @@ -264,6 +264,11 @@ jobs: # fails ("SyntaxError: Unexpected end of JSON input" — CI run # 38114723635). Pre-rendering freezes Julia/*.qmd into the workspace # _freeze so babelquarto never executes them. + - name: Create temporary directory + run: | + sudo mkdir -p ${{ env.TMP_DIR }} + sudo chown -R $USER:$USER ${{ env.TMP_DIR }} + - name: Pre-render Julia notebooks env: QUARTO_JULIA_PROJECT: ${{ env.QUARTO_JULIA_PROJECT }}