From 9233e62ac369cce9ca6b4cf8f8f8e485f9775746 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Thu, 19 Mar 2026 11:37:37 +0100 Subject: [PATCH 1/6] Squashed 'src/resources/extension-subtrees/julia-engine/' changes from 1de70acbd..605d04dfd 605d04dfd Merge pull request #5 from PumasAI/jk/repo-cleanup 883587548 docs: add AGENTS.md/CLAUDE.md and update README 1bbc55d0b chore: remove obsolete files and clean up gitignore git-subtree-dir: src/resources/extension-subtrees/julia-engine git-subtree-split: 605d04dfd5db65a072aa6d7afed2ce5ad83c8514 --- .gitignore | 5 +- AGENTS.md | 44 ++ CLAUDE.md | 1 + Manifest.toml | 1571 ------------------------------------------------- Project.toml | 4 - README.md | 12 +- example.qmd | 80 --- 7 files changed, 48 insertions(+), 1669 deletions(-) create mode 100644 AGENTS.md create mode 120000 CLAUDE.md delete mode 100644 Manifest.toml delete mode 100644 Project.toml delete mode 100644 example.qmd diff --git a/.gitignore b/.gitignore index 844b2d77f9..6d3ece8daa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,3 @@ -example.html -example_files/ +.DS_Store .quarto/ - -/.quarto/ **/*.quarto_ipynb diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000..9f5824e830 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,44 @@ +# quarto-julia-engine + +This repo contains the Julia execution engine for Quarto. The engine was originally built directly into quarto-cli but has since been refactored into an extension-style engine, moved into this separate repo, and is pulled into quarto-cli via a git subtree at `src/resources/extension-subtrees/julia-engine/`. + +The engine extension requires a compatible version of quarto (>= 1.9.0). If work involves changes to the engine–quarto interface, you'll also need a corresponding dev build of quarto-cli with matching changes. + +## Repo structure + +- `_extensions/julia-engine/` — the actual extension (contains `_extension.yml` and the bundled `julia-engine.js`) +- `src/` — TypeScript source for the engine (`julia-engine.ts`, `constants.ts`). Changes here must be bundled into `_extensions/julia-engine/julia-engine.js` to take effect. +- `_quarto.yml` — makes the repo root a quarto project so rendering picks up the extension from `_extensions/` + +## Building + +After editing the TypeScript source in `src/`, rebuild the bundled JS: + +```sh +quarto call build-ts-extension src/julia-engine.ts +``` + +This bundles `src/julia-engine.ts` into `_extensions/julia-engine/julia-engine.js`. + +## Testing locally + +### Quick: render in this repo + +Create or edit a `.qmd` file in the repo root with `engine: julia` and render it: + +```sh +quarto render some-file.qmd +``` + +Since this directory is a quarto project with the extension in `_extensions/`, quarto discovers and uses the engine from here. + +### Full: run quarto-cli's tests + +To test with quarto-cli's own test suite, push the current state into the subtree in a local quarto-cli clone: + +```sh +cd /path/to/quarto-cli +git subtree pull --prefix=src/resources/extension-subtrees/julia-engine /path/to/quarto-julia-engine --squash +``` + +Then run quarto-cli's tests as usual. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 0000000000..47dc3e3d86 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file diff --git a/Manifest.toml b/Manifest.toml deleted file mode 100644 index a7afb0a8f1..0000000000 --- a/Manifest.toml +++ /dev/null @@ -1,1571 +0,0 @@ -# This file is machine-generated - editing it directly is not advised - -julia_version = "1.12.1" -manifest_format = "2.0" -project_hash = "8551d9d97d1c74b8788a9cb955d4fe3906e3f1e7" - -[[deps.AbstractFFTs]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "d92ad398961a3ed262d8bf04a1a2b8340f915fef" -uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c" -version = "1.5.0" -weakdeps = ["ChainRulesCore", "Test"] - - [deps.AbstractFFTs.extensions] - AbstractFFTsChainRulesCoreExt = "ChainRulesCore" - AbstractFFTsTestExt = "Test" - -[[deps.Adapt]] -deps = ["LinearAlgebra", "Requires"] -git-tree-sha1 = "7e35fca2bdfba44d797c53dfe63a51fabf39bfc0" -uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" -version = "4.4.0" -weakdeps = ["SparseArrays", "StaticArrays"] - - [deps.Adapt.extensions] - AdaptSparseArraysExt = "SparseArrays" - AdaptStaticArraysExt = "StaticArrays" - -[[deps.AliasTables]] -deps = ["PtrArrays", "Random"] -git-tree-sha1 = "9876e1e164b144ca45e9e3198d0b689cadfed9ff" -uuid = "66dad0bd-aa9a-41b7-9441-69ab47430ed8" -version = "1.1.3" - -[[deps.ArgTools]] -uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" -version = "1.1.2" - -[[deps.Arpack]] -deps = ["Arpack_jll", "Libdl", "LinearAlgebra", "Logging"] -git-tree-sha1 = "9b9b347613394885fd1c8c7729bfc60528faa436" -uuid = "7d9fca2a-8960-54d3-9f78-7d1dccf2cb97" -version = "0.5.4" - -[[deps.Arpack_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "OpenBLAS_jll", "Pkg"] -git-tree-sha1 = "5ba6c757e8feccf03a1554dfaf3e26b3cfc7fd5e" -uuid = "68821587-b530-5797-8361-c406ea357684" -version = "3.5.1+1" - -[[deps.Artifacts]] -uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" -version = "1.11.0" - -[[deps.AxisAlgorithms]] -deps = ["LinearAlgebra", "Random", "SparseArrays", "WoodburyMatrices"] -git-tree-sha1 = "01b8ccb13d68535d73d2b0c23e39bd23155fb712" -uuid = "13072b0f-2c55-5437-9ae7-d433b7a33950" -version = "1.1.0" - -[[deps.Base64]] -uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" -version = "1.11.0" - -[[deps.BitFlags]] -git-tree-sha1 = "0691e34b3bb8be9307330f88d1a3c3f25466c24d" -uuid = "d1d4a3ce-64b1-5f1a-9ba4-7e7e69966f35" -version = "0.1.9" - -[[deps.Bzip2_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "1b96ea4a01afe0ea4090c5c8039690672dd13f2e" -uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0" -version = "1.0.9+0" - -[[deps.CSV]] -deps = ["CodecZlib", "Dates", "FilePathsBase", "InlineStrings", "Mmap", "Parsers", "PooledArrays", "PrecompileTools", "SentinelArrays", "Tables", "Unicode", "WeakRefStrings", "WorkerUtilities"] -git-tree-sha1 = "deddd8725e5e1cc49ee205a1964256043720a6c3" -uuid = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" -version = "0.10.15" - -[[deps.Cairo_jll]] -deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] -git-tree-sha1 = "fde3bf89aead2e723284a8ff9cdf5b551ed700e8" -uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a" -version = "1.18.5+0" - -[[deps.CategoricalArrays]] -deps = ["DataAPI", "Future", "Missings", "Printf", "Requires", "Statistics", "Unicode"] -git-tree-sha1 = "5084cc1a28976dd1642c9f337b28a3cb03e0f7d2" -uuid = "324d7699-5711-5eae-9e2f-1d82baa6b597" -version = "0.10.7" - -[[deps.ChainRulesCore]] -deps = ["Compat", "LinearAlgebra"] -git-tree-sha1 = "e4c6a16e77171a5f5e25e9646617ab1c276c5607" -uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" -version = "1.26.0" -weakdeps = ["SparseArrays"] - - [deps.ChainRulesCore.extensions] - ChainRulesCoreSparseArraysExt = "SparseArrays" - -[[deps.Clustering]] -deps = ["Distances", "LinearAlgebra", "NearestNeighbors", "Printf", "Random", "SparseArrays", "Statistics", "StatsBase"] -git-tree-sha1 = "3e22db924e2945282e70c33b75d4dde8bfa44c94" -uuid = "aaaa29a8-35af-508c-8bc3-b662a17a0fe5" -version = "0.15.8" - -[[deps.CodecZlib]] -deps = ["TranscodingStreams", "Zlib_jll"] -git-tree-sha1 = "962834c22b66e32aa10f7611c08c8ca4e20749a9" -uuid = "944b1d66-785c-5afd-91f1-9de20f533193" -version = "0.7.8" - -[[deps.ColorSchemes]] -deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "PrecompileTools", "Random"] -git-tree-sha1 = "b0fd3f56fa442f81e0a47815c92245acfaaa4e34" -uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4" -version = "3.31.0" - -[[deps.ColorTypes]] -deps = ["FixedPointNumbers", "Random"] -git-tree-sha1 = "67e11ee83a43eb71ddc950302c53bf33f0690dfe" -uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" -version = "0.12.1" -weakdeps = ["StyledStrings"] - - [deps.ColorTypes.extensions] - StyledStringsExt = "StyledStrings" - -[[deps.ColorVectorSpace]] -deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "Requires", "Statistics", "TensorCore"] -git-tree-sha1 = "8b3b6f87ce8f65a2b4f857528fd8d70086cd72b1" -uuid = "c3611d14-8923-5661-9e6a-0046d554d3a4" -version = "0.11.0" -weakdeps = ["SpecialFunctions"] - - [deps.ColorVectorSpace.extensions] - SpecialFunctionsExt = "SpecialFunctions" - -[[deps.Colors]] -deps = ["ColorTypes", "FixedPointNumbers", "Reexport"] -git-tree-sha1 = "37ea44092930b1811e666c3bc38065d7d87fcc74" -uuid = "5ae59095-9a9b-59fe-a467-6f913c188581" -version = "0.13.1" - -[[deps.Compat]] -deps = ["TOML", "UUIDs"] -git-tree-sha1 = "9d8a54ce4b17aa5bdce0ea5c34bc5e7c340d16ad" -uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "4.18.1" -weakdeps = ["Dates", "LinearAlgebra"] - - [deps.Compat.extensions] - CompatLinearAlgebraExt = "LinearAlgebra" - -[[deps.CompilerSupportLibraries_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" -version = "1.3.0+1" - -[[deps.ConcurrentUtilities]] -deps = ["Serialization", "Sockets"] -git-tree-sha1 = "d9d26935a0bcffc87d2613ce14c527c99fc543fd" -uuid = "f0e56b4a-5159-44fe-b623-3e5288b988bb" -version = "2.5.0" - -[[deps.Contour]] -git-tree-sha1 = "439e35b0b36e2e5881738abc8857bd92ad6ff9a8" -uuid = "d38c429a-6771-53c6-b99e-75d170b6e991" -version = "0.6.3" - -[[deps.Crayons]] -git-tree-sha1 = "249fe38abf76d48563e2f4556bebd215aa317e15" -uuid = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f" -version = "4.1.1" - -[[deps.DataAPI]] -git-tree-sha1 = "abe83f3a2f1b857aac70ef8b269080af17764bbe" -uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" -version = "1.16.0" - -[[deps.DataFrames]] -deps = ["Compat", "DataAPI", "DataStructures", "Future", "InlineStrings", "InvertedIndices", "IteratorInterfaceExtensions", "LinearAlgebra", "Markdown", "Missings", "PooledArrays", "PrecompileTools", "PrettyTables", "Printf", "Random", "Reexport", "SentinelArrays", "SortingAlgorithms", "Statistics", "TableTraits", "Tables", "Unicode"] -git-tree-sha1 = "d8928e9169ff76c6281f39a659f9bca3a573f24c" -uuid = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" -version = "1.8.1" - -[[deps.DataStructures]] -deps = ["OrderedCollections"] -git-tree-sha1 = "6c72198e6a101cccdd4c9731d3985e904ba26037" -uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" -version = "0.19.1" - -[[deps.DataValueInterfaces]] -git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6" -uuid = "e2d170a0-9d28-54be-80f0-106bbe20a464" -version = "1.0.0" - -[[deps.Dates]] -deps = ["Printf"] -uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" -version = "1.11.0" - -[[deps.Dbus_jll]] -deps = ["Artifacts", "Expat_jll", "JLLWrappers", "Libdl"] -git-tree-sha1 = "473e9afc9cf30814eb67ffa5f2db7df82c3ad9fd" -uuid = "ee1fde0b-3d02-5ea6-8484-8dfef6360eab" -version = "1.16.2+0" - -[[deps.DelimitedFiles]] -deps = ["Mmap"] -git-tree-sha1 = "9e2f36d3c96a820c678f2f1f1782582fcf685bae" -uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" -version = "1.9.1" - -[[deps.Distances]] -deps = ["LinearAlgebra", "Statistics", "StatsAPI"] -git-tree-sha1 = "c7e3a542b999843086e2f29dac96a618c105be1d" -uuid = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7" -version = "0.10.12" -weakdeps = ["ChainRulesCore", "SparseArrays"] - - [deps.Distances.extensions] - DistancesChainRulesCoreExt = "ChainRulesCore" - DistancesSparseArraysExt = "SparseArrays" - -[[deps.Distributed]] -deps = ["Random", "Serialization", "Sockets"] -uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" -version = "1.11.0" - -[[deps.Distributions]] -deps = ["AliasTables", "FillArrays", "LinearAlgebra", "PDMats", "Printf", "QuadGK", "Random", "SpecialFunctions", "Statistics", "StatsAPI", "StatsBase", "StatsFuns"] -git-tree-sha1 = "3bc002af51045ca3b47d2e1787d6ce02e68b943a" -uuid = "31c24e10-a181-5473-b8eb-7969acd0382f" -version = "0.25.122" - - [deps.Distributions.extensions] - DistributionsChainRulesCoreExt = "ChainRulesCore" - DistributionsDensityInterfaceExt = "DensityInterface" - DistributionsTestExt = "Test" - - [deps.Distributions.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - DensityInterface = "b429d917-457f-4dbc-8f4c-0cc954292b1d" - Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" - -[[deps.DocStringExtensions]] -git-tree-sha1 = "7442a5dfe1ebb773c29cc2962a8980f47221d76c" -uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" -version = "0.9.5" - -[[deps.Downloads]] -deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] -uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" -version = "1.6.0" - -[[deps.EpollShim_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "8a4be429317c42cfae6a7fc03c31bad1970c310d" -uuid = "2702e6a9-849d-5ed8-8c21-79e8b8f9ee43" -version = "0.0.20230411+1" - -[[deps.ExceptionUnwrapping]] -deps = ["Test"] -git-tree-sha1 = "d36f682e590a83d63d1c7dbd287573764682d12a" -uuid = "460bff9d-24e4-43bc-9d9f-a8973cb893f4" -version = "0.1.11" - -[[deps.Expat_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "27af30de8b5445644e8ffe3bcb0d72049c089cf1" -uuid = "2e619515-83b5-522b-bb60-26c02a35a201" -version = "2.7.3+0" - -[[deps.ExprTools]] -git-tree-sha1 = "27415f162e6028e81c72b82ef756bf321213b6ec" -uuid = "e2ba6199-217a-4e67-a87a-7c52f15ade04" -version = "0.1.10" - -[[deps.FFMPEG]] -deps = ["FFMPEG_jll"] -git-tree-sha1 = "95ecf07c2eea562b5adbd0696af6db62c0f52560" -uuid = "c87230d0-a227-11e9-1b43-d7ebe4e7570a" -version = "0.4.5" - -[[deps.FFMPEG_jll]] -deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "JLLWrappers", "LAME_jll", "Libdl", "Ogg_jll", "OpenSSL_jll", "Opus_jll", "PCRE2_jll", "Zlib_jll", "libaom_jll", "libass_jll", "libfdk_aac_jll", "libvorbis_jll", "x264_jll", "x265_jll"] -git-tree-sha1 = "ccc81ba5e42497f4e76553a5545665eed577a663" -uuid = "b22a6f82-2f65-5046-a5b2-351ab43fb4e5" -version = "8.0.0+0" - -[[deps.FFTW]] -deps = ["AbstractFFTs", "FFTW_jll", "Libdl", "LinearAlgebra", "MKL_jll", "Preferences", "Reexport"] -git-tree-sha1 = "97f08406df914023af55ade2f843c39e99c5d969" -uuid = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" -version = "1.10.0" - -[[deps.FFTW_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "6d6219a004b8cf1e0b4dbe27a2860b8e04eba0be" -uuid = "f5851436-0d7a-5f13-b9de-f02708fd171a" -version = "3.3.11+0" - -[[deps.FileIO]] -deps = ["Pkg", "Requires", "UUIDs"] -git-tree-sha1 = "d60eb76f37d7e5a40cc2e7c36974d864b82dc802" -uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" -version = "1.17.1" -weakdeps = ["HTTP"] - - [deps.FileIO.extensions] - HTTPExt = "HTTP" - -[[deps.FilePathsBase]] -deps = ["Compat", "Dates"] -git-tree-sha1 = "3bab2c5aa25e7840a4b065805c0cdfc01f3068d2" -uuid = "48062228-2e41-5def-b9a4-89aafe57970f" -version = "0.9.24" -weakdeps = ["Mmap", "Test"] - - [deps.FilePathsBase.extensions] - FilePathsBaseMmapExt = "Mmap" - FilePathsBaseTestExt = "Test" - -[[deps.FileWatching]] -uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" -version = "1.11.0" - -[[deps.FillArrays]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "173e4d8f14230a7523ae11b9a3fa9edb3e0efd78" -uuid = "1a297f60-69ca-5386-bcde-b61e274b549b" -version = "1.14.0" -weakdeps = ["PDMats", "SparseArrays", "Statistics"] - - [deps.FillArrays.extensions] - FillArraysPDMatsExt = "PDMats" - FillArraysSparseArraysExt = "SparseArrays" - FillArraysStatisticsExt = "Statistics" - -[[deps.FixedPointNumbers]] -deps = ["Statistics"] -git-tree-sha1 = "05882d6995ae5c12bb5f36dd2ed3f61c98cbb172" -uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" -version = "0.8.5" - -[[deps.Fontconfig_jll]] -deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Zlib_jll"] -git-tree-sha1 = "f85dac9a96a01087df6e3a749840015a0ca3817d" -uuid = "a3f928ae-7b40-5064-980b-68af3947d34b" -version = "2.17.1+0" - -[[deps.Format]] -git-tree-sha1 = "9c68794ef81b08086aeb32eeaf33531668d5f5fc" -uuid = "1fa38f19-a742-5d3f-a2b9-30dd87b9d5f8" -version = "1.3.7" - -[[deps.FreeType2_jll]] -deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Zlib_jll"] -git-tree-sha1 = "2c5512e11c791d1baed2049c5652441b28fc6a31" -uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7" -version = "2.13.4+0" - -[[deps.FriBidi_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "7a214fdac5ed5f59a22c2d9a885a16da1c74bbc7" -uuid = "559328eb-81f9-559d-9380-de523a88c83c" -version = "1.0.17+0" - -[[deps.Future]] -deps = ["Random"] -uuid = "9fa8497b-333b-5362-9e8d-4d0656e87820" -version = "1.11.0" - -[[deps.GLFW_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Xorg_libXcursor_jll", "Xorg_libXi_jll", "Xorg_libXinerama_jll", "Xorg_libXrandr_jll", "libdecor_jll", "xkbcommon_jll"] -git-tree-sha1 = "fcb0584ff34e25155876418979d4c8971243bb89" -uuid = "0656b61e-2033-5cc2-a64a-77c0f6c09b89" -version = "3.4.0+2" - -[[deps.GR]] -deps = ["Artifacts", "Base64", "DelimitedFiles", "Downloads", "GR_jll", "HTTP", "JSON", "Libdl", "LinearAlgebra", "Preferences", "Printf", "Qt6Wayland_jll", "Random", "Serialization", "Sockets", "TOML", "Tar", "Test", "p7zip_jll"] -git-tree-sha1 = "1828eb7275491981fa5f1752a5e126e8f26f8741" -uuid = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71" -version = "0.73.17" - -[[deps.GR_jll]] -deps = ["Artifacts", "Bzip2_jll", "Cairo_jll", "FFMPEG_jll", "Fontconfig_jll", "FreeType2_jll", "GLFW_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libtiff_jll", "Pixman_jll", "Qt6Base_jll", "Zlib_jll", "libpng_jll"] -git-tree-sha1 = "27299071cc29e409488ada41ec7643e0ab19091f" -uuid = "d2c73de3-f751-5644-a686-071e5b155ba9" -version = "0.73.17+0" - -[[deps.GettextRuntime_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll"] -git-tree-sha1 = "45288942190db7c5f760f59c04495064eedf9340" -uuid = "b0724c58-0f36-5564-988d-3bb0596ebc4a" -version = "0.22.4+0" - -[[deps.Ghostscript_jll]] -deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Zlib_jll"] -git-tree-sha1 = "38044a04637976140074d0b0621c1edf0eb531fd" -uuid = "61579ee1-b43e-5ca0-a5da-69d92c66a64b" -version = "9.55.1+0" - -[[deps.Glib_jll]] -deps = ["Artifacts", "GettextRuntime_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE2_jll", "Zlib_jll"] -git-tree-sha1 = "50c11ffab2a3d50192a228c313f05b5b5dc5acb2" -uuid = "7746bdde-850d-59dc-9ae8-88ece973131d" -version = "2.86.0+0" - -[[deps.Graphite2_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "8a6dbda1fd736d60cc477d99f2e7a042acfa46e8" -uuid = "3b182d85-2403-5c21-9c21-1e1f0cc25472" -version = "1.3.15+0" - -[[deps.Grisu]] -git-tree-sha1 = "53bb909d1151e57e2484c3d1b53e19552b887fb2" -uuid = "42e2da0e-8278-4e71-bc24-59509adca0fe" -version = "1.0.2" - -[[deps.HTTP]] -deps = ["Base64", "CodecZlib", "ConcurrentUtilities", "Dates", "ExceptionUnwrapping", "Logging", "LoggingExtras", "MbedTLS", "NetworkOptions", "OpenSSL", "PrecompileTools", "Random", "SimpleBufferStream", "Sockets", "URIs", "UUIDs"] -git-tree-sha1 = "5e6fe50ae7f23d171f44e311c2960294aaa0beb5" -uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3" -version = "1.10.19" - -[[deps.HarfBuzz_jll]] -deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "Graphite2_jll", "JLLWrappers", "Libdl", "Libffi_jll"] -git-tree-sha1 = "f923f9a774fcf3f5cb761bfa43aeadd689714813" -uuid = "2e76f6c2-a576-52d4-95c1-20adfe4de566" -version = "8.5.1+0" - -[[deps.HypergeometricFunctions]] -deps = ["LinearAlgebra", "OpenLibm_jll", "SpecialFunctions"] -git-tree-sha1 = "68c173f4f449de5b438ee67ed0c9c748dc31a2ec" -uuid = "34004b35-14d8-5ef3-9330-4cdb6864b03a" -version = "0.3.28" - -[[deps.InlineStrings]] -git-tree-sha1 = "8f3d257792a522b4601c24a577954b0a8cd7334d" -uuid = "842dd82b-1e85-43dc-bf29-5d0ee9dffc48" -version = "1.4.5" - - [deps.InlineStrings.extensions] - ArrowTypesExt = "ArrowTypes" - ParsersExt = "Parsers" - - [deps.InlineStrings.weakdeps] - ArrowTypes = "31f734f8-188a-4ce0-8406-c8a06bd891cd" - Parsers = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" - -[[deps.IntelOpenMP_jll]] -deps = ["Artifacts", "JLLWrappers", "LazyArtifacts", "Libdl"] -git-tree-sha1 = "ec1debd61c300961f98064cfb21287613ad7f303" -uuid = "1d5cc7b8-4909-519e-a0f8-d0f5ad9712d0" -version = "2025.2.0+0" - -[[deps.InteractiveUtils]] -deps = ["Markdown"] -uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" -version = "1.11.0" - -[[deps.Interpolations]] -deps = ["Adapt", "AxisAlgorithms", "ChainRulesCore", "LinearAlgebra", "OffsetArrays", "Random", "Ratios", "SharedArrays", "SparseArrays", "StaticArrays", "WoodburyMatrices"] -git-tree-sha1 = "65d505fa4c0d7072990d659ef3fc086eb6da8208" -uuid = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59" -version = "0.16.2" - - [deps.Interpolations.extensions] - InterpolationsForwardDiffExt = "ForwardDiff" - InterpolationsUnitfulExt = "Unitful" - - [deps.Interpolations.weakdeps] - ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" - Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" - -[[deps.InvertedIndices]] -git-tree-sha1 = "6da3c4316095de0f5ee2ebd875df8721e7e0bdbe" -uuid = "41ab1584-1d38-5bbf-9106-f11c6c58b48f" -version = "1.3.1" - -[[deps.IrrationalConstants]] -git-tree-sha1 = "b2d91fe939cae05960e760110b328288867b5758" -uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" -version = "0.2.6" - -[[deps.IteratorInterfaceExtensions]] -git-tree-sha1 = "a3f24677c21f5bbe9d2a714f95dcd58337fb2856" -uuid = "82899510-4779-5014-852e-03e436cf321d" -version = "1.0.0" - -[[deps.JLFzf]] -deps = ["REPL", "Random", "fzf_jll"] -git-tree-sha1 = "82f7acdc599b65e0f8ccd270ffa1467c21cb647b" -uuid = "1019f520-868f-41f5-a6de-eb00f4b6a39c" -version = "0.1.11" - -[[deps.JLLWrappers]] -deps = ["Artifacts", "Preferences"] -git-tree-sha1 = "0533e564aae234aff59ab625543145446d8b6ec2" -uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" -version = "1.7.1" - -[[deps.JSON]] -deps = ["Dates", "Logging", "Parsers", "PrecompileTools", "StructUtils", "UUIDs", "Unicode"] -git-tree-sha1 = "06ea418d0c95878c8f3031023951edcf25b9e0ef" -uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" -version = "1.2.0" - - [deps.JSON.extensions] - JSONArrowExt = ["ArrowTypes"] - - [deps.JSON.weakdeps] - ArrowTypes = "31f734f8-188a-4ce0-8406-c8a06bd891cd" - -[[deps.JpegTurbo_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "4255f0032eafd6451d707a51d5f0248b8a165e4d" -uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" -version = "3.1.3+0" - -[[deps.JuliaSyntaxHighlighting]] -deps = ["StyledStrings"] -uuid = "ac6e5ff7-fb65-4e79-a425-ec3bc9c03011" -version = "1.12.0" - -[[deps.KernelDensity]] -deps = ["Distributions", "DocStringExtensions", "FFTW", "Interpolations", "StatsBase"] -git-tree-sha1 = "ba51324b894edaf1df3ab16e2cc6bc3280a2f1a7" -uuid = "5ab0869b-81aa-558d-bb23-cbf5423bbe9b" -version = "0.6.10" - -[[deps.LAME_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "059aabebaa7c82ccb853dd4a0ee9d17796f7e1bc" -uuid = "c1c5ebd0-6772-5130-a774-d5fcae4a789d" -version = "3.100.3+0" - -[[deps.LERC_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "aaafe88dccbd957a8d82f7d05be9b69172e0cee3" -uuid = "88015f11-f218-50d7-93a8-a6af411a945d" -version = "4.0.1+0" - -[[deps.LLVMOpenMP_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "eb62a3deb62fc6d8822c0c4bef73e4412419c5d8" -uuid = "1d63c593-3942-5779-bab2-d838dc0a180e" -version = "18.1.8+0" - -[[deps.LZO_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "1c602b1127f4751facb671441ca72715cc95938a" -uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac" -version = "2.10.3+0" - -[[deps.LaTeXStrings]] -git-tree-sha1 = "dda21b8cbd6a6c40d9d02a73230f9d70fed6918c" -uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" -version = "1.4.0" - -[[deps.Latexify]] -deps = ["Format", "Ghostscript_jll", "InteractiveUtils", "LaTeXStrings", "MacroTools", "Markdown", "OrderedCollections", "Requires"] -git-tree-sha1 = "44f93c47f9cd6c7e431f2f2091fcba8f01cd7e8f" -uuid = "23fbe1c1-3f47-55db-b15f-69d7ec21a316" -version = "0.16.10" - - [deps.Latexify.extensions] - DataFramesExt = "DataFrames" - SparseArraysExt = "SparseArrays" - SymEngineExt = "SymEngine" - TectonicExt = "tectonic_jll" - - [deps.Latexify.weakdeps] - DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" - SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" - SymEngine = "123dc426-2d89-5057-bbad-38513e3affd8" - tectonic_jll = "d7dd28d6-a5e6-559c-9131-7eb760cdacc5" - -[[deps.LazyArtifacts]] -deps = ["Artifacts", "Pkg"] -uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3" -version = "1.11.0" - -[[deps.LibCURL]] -deps = ["LibCURL_jll", "MozillaCACerts_jll"] -uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" -version = "0.6.4" - -[[deps.LibCURL_jll]] -deps = ["Artifacts", "LibSSH2_jll", "Libdl", "OpenSSL_jll", "Zlib_jll", "nghttp2_jll"] -uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" -version = "8.11.1+1" - -[[deps.LibGit2]] -deps = ["LibGit2_jll", "NetworkOptions", "Printf", "SHA"] -uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" -version = "1.11.0" - -[[deps.LibGit2_jll]] -deps = ["Artifacts", "LibSSH2_jll", "Libdl", "OpenSSL_jll"] -uuid = "e37daf67-58a4-590a-8e99-b0245dd2ffc5" -version = "1.9.0+0" - -[[deps.LibSSH2_jll]] -deps = ["Artifacts", "Libdl", "OpenSSL_jll"] -uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" -version = "1.11.3+1" - -[[deps.Libdl]] -uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" -version = "1.11.0" - -[[deps.Libffi_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "c8da7e6a91781c41a863611c7e966098d783c57a" -uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490" -version = "3.4.7+0" - -[[deps.Libglvnd_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll", "Xorg_libXext_jll"] -git-tree-sha1 = "d36c21b9e7c172a44a10484125024495e2625ac0" -uuid = "7e76a0d4-f3c7-5321-8279-8d96eeed0f29" -version = "1.7.1+1" - -[[deps.Libiconv_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "be484f5c92fad0bd8acfef35fe017900b0b73809" -uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531" -version = "1.18.0+0" - -[[deps.Libmount_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "3acf07f130a76f87c041cfb2ff7d7284ca67b072" -uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9" -version = "2.41.2+0" - -[[deps.Libtiff_jll]] -deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "LERC_jll", "Libdl", "XZ_jll", "Zlib_jll", "Zstd_jll"] -git-tree-sha1 = "f04133fe05eff1667d2054c53d59f9122383fe05" -uuid = "89763e89-9b03-5906-acba-b20f662cd828" -version = "4.7.2+0" - -[[deps.Libuuid_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "2a7a12fc0a4e7fb773450d17975322aa77142106" -uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700" -version = "2.41.2+0" - -[[deps.LinearAlgebra]] -deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"] -uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" -version = "1.12.0" - -[[deps.LogExpFunctions]] -deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] -git-tree-sha1 = "13ca9e2586b89836fd20cccf56e57e2b9ae7f38f" -uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" -version = "0.3.29" - - [deps.LogExpFunctions.extensions] - LogExpFunctionsChainRulesCoreExt = "ChainRulesCore" - LogExpFunctionsChangesOfVariablesExt = "ChangesOfVariables" - LogExpFunctionsInverseFunctionsExt = "InverseFunctions" - - [deps.LogExpFunctions.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - ChangesOfVariables = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0" - InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" - -[[deps.Logging]] -uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" -version = "1.11.0" - -[[deps.LoggingExtras]] -deps = ["Dates", "Logging"] -git-tree-sha1 = "f00544d95982ea270145636c181ceda21c4e2575" -uuid = "e6f89c97-d47a-5376-807f-9c37f3926c36" -version = "1.2.0" - -[[deps.MKL_jll]] -deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "oneTBB_jll"] -git-tree-sha1 = "282cadc186e7b2ae0eeadbd7a4dffed4196ae2aa" -uuid = "856f044c-d86e-5d09-b602-aeab76dc8ba7" -version = "2025.2.0+0" - -[[deps.MacroTools]] -git-tree-sha1 = "1e0228a030642014fe5cfe68c2c0a818f9e3f522" -uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" -version = "0.5.16" - -[[deps.Markdown]] -deps = ["Base64", "JuliaSyntaxHighlighting", "StyledStrings"] -uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" -version = "1.11.0" - -[[deps.MbedTLS]] -deps = ["Dates", "MbedTLS_jll", "MozillaCACerts_jll", "NetworkOptions", "Random", "Sockets"] -git-tree-sha1 = "c067a280ddc25f196b5e7df3877c6b226d390aaf" -uuid = "739be429-bea8-5141-9913-cc70e7f3736d" -version = "1.1.9" - -[[deps.MbedTLS_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "3cce3511ca2c6f87b19c34ffc623417ed2798cbd" -uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" -version = "2.28.10+0" - -[[deps.Measures]] -git-tree-sha1 = "c13304c81eec1ed3af7fc20e75fb6b26092a1102" -uuid = "442fdcdd-2543-5da2-b0f3-8c86c306513e" -version = "0.3.2" - -[[deps.Missings]] -deps = ["DataAPI"] -git-tree-sha1 = "ec4f7fbeab05d7747bdf98eb74d130a2a2ed298d" -uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28" -version = "1.2.0" - -[[deps.Mmap]] -uuid = "a63ad114-7e13-5084-954f-fe012c677804" -version = "1.11.0" - -[[deps.Mocking]] -deps = ["Compat", "ExprTools"] -git-tree-sha1 = "2c140d60d7cb82badf06d8783800d0bcd1a7daa2" -uuid = "78c3b35d-d492-501b-9361-3d52fe80e533" -version = "0.8.1" - -[[deps.MozillaCACerts_jll]] -uuid = "14a3606d-f60d-562e-9121-12d972cd8159" -version = "2025.5.20" - -[[deps.MultivariateStats]] -deps = ["Arpack", "Distributions", "LinearAlgebra", "SparseArrays", "Statistics", "StatsAPI", "StatsBase"] -git-tree-sha1 = "816620e3aac93e5b5359e4fdaf23ca4525b00ddf" -uuid = "6f286f6a-111f-5878-ab1e-185364afe411" -version = "0.10.3" - -[[deps.NaNMath]] -deps = ["OpenLibm_jll"] -git-tree-sha1 = "9b8215b1ee9e78a293f99797cd31375471b2bcae" -uuid = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3" -version = "1.1.3" - -[[deps.NearestNeighbors]] -deps = ["Distances", "StaticArrays"] -git-tree-sha1 = "ca7e18198a166a1f3eb92a3650d53d94ed8ca8a1" -uuid = "b8a86587-4115-5ab1-83bc-aa920d37bbce" -version = "0.4.22" - -[[deps.NetworkOptions]] -uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" -version = "1.3.0" - -[[deps.Observables]] -git-tree-sha1 = "7438a59546cf62428fc9d1bc94729146d37a7225" -uuid = "510215fc-4207-5dde-b226-833fc4488ee2" -version = "0.5.5" - -[[deps.OffsetArrays]] -git-tree-sha1 = "117432e406b5c023f665fa73dc26e79ec3630151" -uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" -version = "1.17.0" -weakdeps = ["Adapt"] - - [deps.OffsetArrays.extensions] - OffsetArraysAdaptExt = "Adapt" - -[[deps.Ogg_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "b6aa4566bb7ae78498a5e68943863fa8b5231b59" -uuid = "e7412a2a-1a6e-54c0-be00-318e2571c051" -version = "1.3.6+0" - -[[deps.OpenBLAS_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] -uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" -version = "0.3.29+0" - -[[deps.OpenLibm_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "05823500-19ac-5b8b-9628-191a04bc5112" -version = "0.8.7+0" - -[[deps.OpenSSL]] -deps = ["BitFlags", "Dates", "MozillaCACerts_jll", "OpenSSL_jll", "Sockets"] -git-tree-sha1 = "f1a7e086c677df53e064e0fdd2c9d0b0833e3f6e" -uuid = "4d8831e6-92b7-49fb-bdf8-b643e874388c" -version = "1.5.0" - -[[deps.OpenSSL_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" -version = "3.5.1+0" - -[[deps.OpenSpecFun_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl"] -git-tree-sha1 = "1346c9208249809840c91b26703912dff463d335" -uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e" -version = "0.5.6+0" - -[[deps.Opus_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "c392fc5dd032381919e3b22dd32d6443760ce7ea" -uuid = "91d4177d-7536-5919-b921-800302f37372" -version = "1.5.2+0" - -[[deps.OrderedCollections]] -git-tree-sha1 = "05868e21324cede2207c6f0f466b4bfef6d5e7ee" -uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" -version = "1.8.1" - -[[deps.PCRE2_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "efcefdf7-47ab-520b-bdef-62a2eaa19f15" -version = "10.44.0+1" - -[[deps.PDMats]] -deps = ["LinearAlgebra", "SparseArrays", "SuiteSparse"] -git-tree-sha1 = "d922b4d80d1e12c658da7785e754f4796cc1d60d" -uuid = "90014a1f-27ba-587c-ab20-58faa44d9150" -version = "0.11.36" -weakdeps = ["StatsBase"] - - [deps.PDMats.extensions] - StatsBaseExt = "StatsBase" - -[[deps.Pango_jll]] -deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "FriBidi_jll", "Glib_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl"] -git-tree-sha1 = "1f7f9bbd5f7a2e5a9f7d96e51c9754454ea7f60b" -uuid = "36c8627f-9965-5494-a995-c6b170f724f3" -version = "1.56.4+0" - -[[deps.Parsers]] -deps = ["Dates", "PrecompileTools", "UUIDs"] -git-tree-sha1 = "7d2f8f21da5db6a806faf7b9b292296da42b2810" -uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" -version = "2.8.3" - -[[deps.Pixman_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LLVMOpenMP_jll", "Libdl"] -git-tree-sha1 = "db76b1ecd5e9715f3d043cec13b2ec93ce015d53" -uuid = "30392449-352a-5448-841d-b1acce4e97dc" -version = "0.44.2+0" - -[[deps.Pkg]] -deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "Random", "SHA", "TOML", "Tar", "UUIDs", "p7zip_jll"] -uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" -version = "1.12.0" -weakdeps = ["REPL"] - - [deps.Pkg.extensions] - REPLExt = "REPL" - -[[deps.PlotThemes]] -deps = ["PlotUtils", "Statistics"] -git-tree-sha1 = "41031ef3a1be6f5bbbf3e8073f210556daeae5ca" -uuid = "ccf2f8ad-2431-5c83-bf29-c5338b663b6a" -version = "3.3.0" - -[[deps.PlotUtils]] -deps = ["ColorSchemes", "Colors", "Dates", "PrecompileTools", "Printf", "Random", "Reexport", "StableRNGs", "Statistics"] -git-tree-sha1 = "3ca9a356cd2e113c420f2c13bea19f8d3fb1cb18" -uuid = "995b91a9-d308-5afd-9ec6-746e21dbc043" -version = "1.4.3" - -[[deps.Plots]] -deps = ["Base64", "Contour", "Dates", "Downloads", "FFMPEG", "FixedPointNumbers", "GR", "JLFzf", "JSON", "LaTeXStrings", "Latexify", "LinearAlgebra", "Measures", "NaNMath", "Pkg", "PlotThemes", "PlotUtils", "PrecompileTools", "Printf", "REPL", "Random", "RecipesBase", "RecipesPipeline", "Reexport", "RelocatableFolders", "Requires", "Scratch", "Showoff", "SparseArrays", "Statistics", "StatsBase", "TOML", "UUIDs", "UnicodeFun", "Unzip"] -git-tree-sha1 = "12ce661880f8e309569074a61d3767e5756a199f" -uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" -version = "1.41.1" - - [deps.Plots.extensions] - FileIOExt = "FileIO" - GeometryBasicsExt = "GeometryBasics" - IJuliaExt = "IJulia" - ImageInTerminalExt = "ImageInTerminal" - UnitfulExt = "Unitful" - - [deps.Plots.weakdeps] - FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" - GeometryBasics = "5c1252a2-5f33-56bf-86c9-59e7332b4326" - IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a" - ImageInTerminal = "d8c32880-2388-543b-8c61-d9f865259254" - Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" - -[[deps.PooledArrays]] -deps = ["DataAPI", "Future"] -git-tree-sha1 = "36d8b4b899628fb92c2749eb488d884a926614d3" -uuid = "2dfb63ee-cc39-5dd5-95bd-886bf059d720" -version = "1.4.3" - -[[deps.PrecompileTools]] -deps = ["Preferences"] -git-tree-sha1 = "07a921781cab75691315adc645096ed5e370cb77" -uuid = "aea7be01-6a6a-4083-8856-8a6e6704d82a" -version = "1.3.3" - -[[deps.Preferences]] -deps = ["TOML"] -git-tree-sha1 = "0f27480397253da18fe2c12a4ba4eb9eb208bf3d" -uuid = "21216c6a-2e73-6563-6e65-726566657250" -version = "1.5.0" - -[[deps.PrettyTables]] -deps = ["Crayons", "LaTeXStrings", "Markdown", "PrecompileTools", "Printf", "REPL", "Reexport", "StringManipulation", "Tables"] -git-tree-sha1 = "6b8e2f0bae3f678811678065c09571c1619da219" -uuid = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d" -version = "3.1.0" - -[[deps.Printf]] -deps = ["Unicode"] -uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" -version = "1.11.0" - -[[deps.PtrArrays]] -git-tree-sha1 = "1d36ef11a9aaf1e8b74dacc6a731dd1de8fd493d" -uuid = "43287f4e-b6f4-7ad1-bb20-aadabca52c3d" -version = "1.3.0" - -[[deps.Qt6Base_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "Fontconfig_jll", "Glib_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "OpenSSL_jll", "Vulkan_Loader_jll", "Xorg_libSM_jll", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Xorg_libxcb_jll", "Xorg_xcb_util_cursor_jll", "Xorg_xcb_util_image_jll", "Xorg_xcb_util_keysyms_jll", "Xorg_xcb_util_renderutil_jll", "Xorg_xcb_util_wm_jll", "Zlib_jll", "libinput_jll", "xkbcommon_jll"] -git-tree-sha1 = "34f7e5d2861083ec7596af8b8c092531facf2192" -uuid = "c0090381-4147-56d7-9ebc-da0b1113ec56" -version = "6.8.2+2" - -[[deps.Qt6Declarative_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Qt6Base_jll", "Qt6ShaderTools_jll"] -git-tree-sha1 = "da7adf145cce0d44e892626e647f9dcbe9cb3e10" -uuid = "629bc702-f1f5-5709-abd5-49b8460ea067" -version = "6.8.2+1" - -[[deps.Qt6ShaderTools_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Qt6Base_jll"] -git-tree-sha1 = "9eca9fc3fe515d619ce004c83c31ffd3f85c7ccf" -uuid = "ce943373-25bb-56aa-8eca-768745ed7b5a" -version = "6.8.2+1" - -[[deps.Qt6Wayland_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Qt6Base_jll", "Qt6Declarative_jll"] -git-tree-sha1 = "8f528b0851b5b7025032818eb5abbeb8a736f853" -uuid = "e99dba38-086e-5de3-a5b1-6e4c66e897c3" -version = "6.8.2+2" - -[[deps.QuadGK]] -deps = ["DataStructures", "LinearAlgebra"] -git-tree-sha1 = "9da16da70037ba9d701192e27befedefb91ec284" -uuid = "1fd47b50-473d-5c70-9696-f719f8f3bcdc" -version = "2.11.2" - - [deps.QuadGK.extensions] - QuadGKEnzymeExt = "Enzyme" - - [deps.QuadGK.weakdeps] - Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" - -[[deps.RData]] -deps = ["CategoricalArrays", "CodecZlib", "DataFrames", "Dates", "FileIO", "Requires", "TimeZones", "Unicode"] -git-tree-sha1 = "19e47a495dfb7240eb44dc6971d660f7e4244a72" -uuid = "df47a6cb-8c03-5eed-afd8-b6050d6c41da" -version = "0.8.3" - -[[deps.RDatasets]] -deps = ["CSV", "CodecZlib", "DataFrames", "FileIO", "Printf", "RData", "Reexport"] -git-tree-sha1 = "2720e6f6afb3e562ccb70a6b62f8f308ff810333" -uuid = "ce6b1742-4840-55fa-b093-852dadbb1d8b" -version = "0.7.7" - -[[deps.REPL]] -deps = ["InteractiveUtils", "JuliaSyntaxHighlighting", "Markdown", "Sockets", "StyledStrings", "Unicode"] -uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" -version = "1.11.0" - -[[deps.Random]] -deps = ["SHA"] -uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" -version = "1.11.0" - -[[deps.Ratios]] -deps = ["Requires"] -git-tree-sha1 = "1342a47bf3260ee108163042310d26f2be5ec90b" -uuid = "c84ed2f1-dad5-54f0-aa8e-dbefe2724439" -version = "0.4.5" -weakdeps = ["FixedPointNumbers"] - - [deps.Ratios.extensions] - RatiosFixedPointNumbersExt = "FixedPointNumbers" - -[[deps.RecipesBase]] -deps = ["PrecompileTools"] -git-tree-sha1 = "5c3d09cc4f31f5fc6af001c250bf1278733100ff" -uuid = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" -version = "1.3.4" - -[[deps.RecipesPipeline]] -deps = ["Dates", "NaNMath", "PlotUtils", "PrecompileTools", "RecipesBase"] -git-tree-sha1 = "45cf9fd0ca5839d06ef333c8201714e888486342" -uuid = "01d81517-befc-4cb6-b9ec-a95719d0359c" -version = "0.6.12" - -[[deps.Reexport]] -git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" -uuid = "189a3867-3050-52da-a836-e630ba90ab69" -version = "1.2.2" - -[[deps.RelocatableFolders]] -deps = ["SHA", "Scratch"] -git-tree-sha1 = "ffdaf70d81cf6ff22c2b6e733c900c3321cab864" -uuid = "05181044-ff0b-4ac5-8273-598c1e38db00" -version = "1.0.1" - -[[deps.Requires]] -deps = ["UUIDs"] -git-tree-sha1 = "62389eeff14780bfe55195b7204c0d8738436d64" -uuid = "ae029012-a4dd-5104-9daa-d747884805df" -version = "1.3.1" - -[[deps.Rmath]] -deps = ["Random", "Rmath_jll"] -git-tree-sha1 = "5b3d50eb374cea306873b371d3f8d3915a018f0b" -uuid = "79098fc4-a85e-5d69-aa6a-4863f24498fa" -version = "0.9.0" - -[[deps.Rmath_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "58cdd8fb2201a6267e1db87ff148dd6c1dbd8ad8" -uuid = "f50d1b31-88e8-58de-be2c-1cc44531875f" -version = "0.5.1+0" - -[[deps.SHA]] -uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" -version = "0.7.0" - -[[deps.Scratch]] -deps = ["Dates"] -git-tree-sha1 = "9b81b8393e50b7d4e6d0a9f14e192294d3b7c109" -uuid = "6c6a2e73-6563-6170-7368-637461726353" -version = "1.3.0" - -[[deps.SentinelArrays]] -deps = ["Dates", "Random"] -git-tree-sha1 = "712fb0231ee6f9120e005ccd56297abbc053e7e0" -uuid = "91c51154-3ec4-41a3-a24f-3f23e20d615c" -version = "1.4.8" - -[[deps.Serialization]] -uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" -version = "1.11.0" - -[[deps.SharedArrays]] -deps = ["Distributed", "Mmap", "Random", "Serialization"] -uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" -version = "1.11.0" - -[[deps.Showoff]] -deps = ["Dates", "Grisu"] -git-tree-sha1 = "91eddf657aca81df9ae6ceb20b959ae5653ad1de" -uuid = "992d4aef-0814-514b-bc4d-f2e9a6c4116f" -version = "1.0.3" - -[[deps.SimpleBufferStream]] -git-tree-sha1 = "f305871d2f381d21527c770d4788c06c097c9bc1" -uuid = "777ac1f9-54b0-4bf8-805c-2214025038e7" -version = "1.2.0" - -[[deps.Sockets]] -uuid = "6462fe0b-24de-5631-8697-dd941f90decc" -version = "1.11.0" - -[[deps.SortingAlgorithms]] -deps = ["DataStructures"] -git-tree-sha1 = "64d974c2e6fdf07f8155b5b2ca2ffa9069b608d9" -uuid = "a2af1166-a08f-5f64-846c-94a0d3cef48c" -version = "1.2.2" - -[[deps.SparseArrays]] -deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"] -uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" -version = "1.12.0" - -[[deps.SpecialFunctions]] -deps = ["IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] -git-tree-sha1 = "f2685b435df2613e25fc10ad8c26dddb8640f547" -uuid = "276daf66-3868-5448-9aa4-cd146d93841b" -version = "2.6.1" -weakdeps = ["ChainRulesCore"] - - [deps.SpecialFunctions.extensions] - SpecialFunctionsChainRulesCoreExt = "ChainRulesCore" - -[[deps.StableRNGs]] -deps = ["Random"] -git-tree-sha1 = "95af145932c2ed859b63329952ce8d633719f091" -uuid = "860ef19b-820b-49d6-a774-d7a799459cd3" -version = "1.0.3" - -[[deps.StaticArrays]] -deps = ["LinearAlgebra", "PrecompileTools", "Random", "StaticArraysCore"] -git-tree-sha1 = "b8693004b385c842357406e3af647701fe783f98" -uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.9.15" -weakdeps = ["ChainRulesCore", "Statistics"] - - [deps.StaticArrays.extensions] - StaticArraysChainRulesCoreExt = "ChainRulesCore" - StaticArraysStatisticsExt = "Statistics" - -[[deps.StaticArraysCore]] -git-tree-sha1 = "6ab403037779dae8c514bad259f32a447262455a" -uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" -version = "1.4.4" - -[[deps.Statistics]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "ae3bb1eb3bba077cd276bc5cfc337cc65c3075c0" -uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" -version = "1.11.1" -weakdeps = ["SparseArrays"] - - [deps.Statistics.extensions] - SparseArraysExt = ["SparseArrays"] - -[[deps.StatsAPI]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "9d72a13a3f4dd3795a195ac5a44d7d6ff5f552ff" -uuid = "82ae8749-77ed-4fe6-ae5f-f523153014b0" -version = "1.7.1" - -[[deps.StatsBase]] -deps = ["AliasTables", "DataAPI", "DataStructures", "LinearAlgebra", "LogExpFunctions", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"] -git-tree-sha1 = "a136f98cefaf3e2924a66bd75173d1c891ab7453" -uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" -version = "0.34.7" - -[[deps.StatsFuns]] -deps = ["HypergeometricFunctions", "IrrationalConstants", "LogExpFunctions", "Reexport", "Rmath", "SpecialFunctions"] -git-tree-sha1 = "91f091a8716a6bb38417a6e6f274602a19aaa685" -uuid = "4c63d2b9-4356-54db-8cca-17b64c39e42c" -version = "1.5.2" - - [deps.StatsFuns.extensions] - StatsFunsChainRulesCoreExt = "ChainRulesCore" - StatsFunsInverseFunctionsExt = "InverseFunctions" - - [deps.StatsFuns.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" - -[[deps.StatsPlots]] -deps = ["AbstractFFTs", "Clustering", "DataStructures", "Distributions", "Interpolations", "KernelDensity", "LinearAlgebra", "MultivariateStats", "NaNMath", "Observables", "Plots", "RecipesBase", "RecipesPipeline", "Reexport", "StatsBase", "TableOperations", "Tables", "Widgets"] -git-tree-sha1 = "88cf3587711d9ad0a55722d339a013c4c56c5bbc" -uuid = "f3b207a7-027a-5e70-b257-86293d7955fd" -version = "0.15.8" - -[[deps.StringManipulation]] -deps = ["PrecompileTools"] -git-tree-sha1 = "725421ae8e530ec29bcbdddbe91ff8053421d023" -uuid = "892a3eda-7b42-436c-8928-eab12a02cf0e" -version = "0.4.1" - -[[deps.StructUtils]] -deps = ["Dates", "UUIDs"] -git-tree-sha1 = "cd47aa083c9c7bdeb7b92de26deb46d6a33163c9" -uuid = "ec057cc2-7a8d-4b58-b3b3-92acb9f63b42" -version = "2.5.1" - - [deps.StructUtils.extensions] - StructUtilsMeasurementsExt = ["Measurements"] - StructUtilsTablesExt = ["Tables"] - - [deps.StructUtils.weakdeps] - Measurements = "eff96d63-e80a-5855-80a2-b1b0885c5ab7" - Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" - -[[deps.StyledStrings]] -uuid = "f489334b-da3d-4c2e-b8f0-e476e12c162b" -version = "1.11.0" - -[[deps.SuiteSparse]] -deps = ["Libdl", "LinearAlgebra", "Serialization", "SparseArrays"] -uuid = "4607b0f0-06f3-5cda-b6b1-a6196a1729e9" - -[[deps.SuiteSparse_jll]] -deps = ["Artifacts", "Libdl", "libblastrampoline_jll"] -uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" -version = "7.8.3+2" - -[[deps.TOML]] -deps = ["Dates"] -uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" -version = "1.0.3" - -[[deps.TZJData]] -deps = ["Artifacts"] -git-tree-sha1 = "72df96b3a595b7aab1e101eb07d2a435963a97e2" -uuid = "dc5dba14-91b3-4cab-a142-028a31da12f7" -version = "1.5.0+2025b" - -[[deps.TableOperations]] -deps = ["SentinelArrays", "Tables", "Test"] -git-tree-sha1 = "e383c87cf2a1dc41fa30c093b2a19877c83e1bc1" -uuid = "ab02a1b2-a7df-11e8-156e-fb1833f50b87" -version = "1.2.0" - -[[deps.TableTraits]] -deps = ["IteratorInterfaceExtensions"] -git-tree-sha1 = "c06b2f539df1c6efa794486abfb6ed2022561a39" -uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c" -version = "1.0.1" - -[[deps.Tables]] -deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "OrderedCollections", "TableTraits"] -git-tree-sha1 = "f2c1efbc8f3a609aadf318094f8fc5204bdaf344" -uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" -version = "1.12.1" - -[[deps.Tar]] -deps = ["ArgTools", "SHA"] -uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" -version = "1.10.0" - -[[deps.TensorCore]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "1feb45f88d133a655e001435632f019a9a1bcdb6" -uuid = "62fd8b95-f654-4bbd-a8a5-9c27f68ccd50" -version = "0.1.1" - -[[deps.Test]] -deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] -uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" -version = "1.11.0" - -[[deps.TimeZones]] -deps = ["Artifacts", "Dates", "Downloads", "InlineStrings", "Mocking", "Printf", "Scratch", "TZJData", "Unicode", "p7zip_jll"] -git-tree-sha1 = "06f4f1f3e8ff09e42e59b043a747332e88e01aba" -uuid = "f269a46b-ccf7-5d73-abea-4c690281aa53" -version = "1.22.1" -weakdeps = ["RecipesBase"] - - [deps.TimeZones.extensions] - TimeZonesRecipesBaseExt = "RecipesBase" - -[[deps.TranscodingStreams]] -git-tree-sha1 = "0c45878dcfdcfa8480052b6ab162cdd138781742" -uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa" -version = "0.11.3" - -[[deps.URIs]] -git-tree-sha1 = "bef26fb046d031353ef97a82e3fdb6afe7f21b1a" -uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4" -version = "1.6.1" - -[[deps.UUIDs]] -deps = ["Random", "SHA"] -uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" -version = "1.11.0" - -[[deps.Unicode]] -uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" -version = "1.11.0" - -[[deps.UnicodeFun]] -deps = ["REPL"] -git-tree-sha1 = "53915e50200959667e78a92a418594b428dffddf" -uuid = "1cfade01-22cf-5700-b092-accc4b62d6e1" -version = "0.4.1" - -[[deps.Unzip]] -git-tree-sha1 = "ca0969166a028236229f63514992fc073799bb78" -uuid = "41fe7b60-77ed-43a1-b4f0-825fd5a5650d" -version = "0.2.0" - -[[deps.Vulkan_Loader_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Wayland_jll", "Xorg_libX11_jll", "Xorg_libXrandr_jll", "xkbcommon_jll"] -git-tree-sha1 = "2f0486047a07670caad3a81a075d2e518acc5c59" -uuid = "a44049a8-05dd-5a78-86c9-5fde0876e88c" -version = "1.3.243+0" - -[[deps.Wayland_jll]] -deps = ["Artifacts", "EpollShim_jll", "Expat_jll", "JLLWrappers", "Libdl", "Libffi_jll"] -git-tree-sha1 = "96478df35bbc2f3e1e791bc7a3d0eeee559e60e9" -uuid = "a2964d1f-97da-50d4-b82a-358c7fce9d89" -version = "1.24.0+0" - -[[deps.WeakRefStrings]] -deps = ["DataAPI", "InlineStrings", "Parsers"] -git-tree-sha1 = "b1be2855ed9ed8eac54e5caff2afcdb442d52c23" -uuid = "ea10d353-3f73-51f8-a26c-33c1cb351aa5" -version = "1.4.2" - -[[deps.Widgets]] -deps = ["Colors", "Dates", "Observables", "OrderedCollections"] -git-tree-sha1 = "e9aeb174f95385de31e70bd15fa066a505ea82b9" -uuid = "cc8bc4a8-27d6-5769-a93b-9d913e69aa62" -version = "0.6.7" - -[[deps.WoodburyMatrices]] -deps = ["LinearAlgebra", "SparseArrays"] -git-tree-sha1 = "c1a7aa6219628fcd757dede0ca95e245c5cd9511" -uuid = "efce3f68-66dc-5838-9240-27a6d6f5f9b6" -version = "1.0.0" - -[[deps.WorkerUtilities]] -git-tree-sha1 = "cd1659ba0d57b71a464a29e64dbc67cfe83d54e7" -uuid = "76eceee3-57b5-4d4a-8e66-0e911cebbf60" -version = "1.6.1" - -[[deps.XZ_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "fee71455b0aaa3440dfdd54a9a36ccef829be7d4" -uuid = "ffd25f8a-64ca-5728-b0f7-c24cf3aae800" -version = "5.8.1+0" - -[[deps.Xorg_libICE_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "a3ea76ee3f4facd7a64684f9af25310825ee3668" -uuid = "f67eecfb-183a-506d-b269-f58e52b52d7c" -version = "1.1.2+0" - -[[deps.Xorg_libSM_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libICE_jll"] -git-tree-sha1 = "9c7ad99c629a44f81e7799eb05ec2746abb5d588" -uuid = "c834827a-8449-5923-a945-d239c165b7dd" -version = "1.2.6+0" - -[[deps.Xorg_libX11_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] -git-tree-sha1 = "b5899b25d17bf1889d25906fb9deed5da0c15b3b" -uuid = "4f6342f7-b3d2-589e-9d20-edeb45f2b2bc" -version = "1.8.12+0" - -[[deps.Xorg_libXau_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "aa1261ebbac3ccc8d16558ae6799524c450ed16b" -uuid = "0c0b7dd1-d40b-584c-a123-a41640f87eec" -version = "1.0.13+0" - -[[deps.Xorg_libXcursor_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libXfixes_jll", "Xorg_libXrender_jll"] -git-tree-sha1 = "6c74ca84bbabc18c4547014765d194ff0b4dc9da" -uuid = "935fb764-8cf2-53bf-bb30-45bb1f8bf724" -version = "1.2.4+0" - -[[deps.Xorg_libXdmcp_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "52858d64353db33a56e13c341d7bf44cd0d7b309" -uuid = "a3789734-cfe1-5b06-b2d0-1dd0d9d62d05" -version = "1.1.6+0" - -[[deps.Xorg_libXext_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] -git-tree-sha1 = "a4c0ee07ad36bf8bbce1c3bb52d21fb1e0b987fb" -uuid = "1082639a-0dae-5f34-9b06-72781eeb8cb3" -version = "1.3.7+0" - -[[deps.Xorg_libXfixes_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] -git-tree-sha1 = "75e00946e43621e09d431d9b95818ee751e6b2ef" -uuid = "d091e8ba-531a-589c-9de9-94069b037ed8" -version = "6.0.2+0" - -[[deps.Xorg_libXi_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libXext_jll", "Xorg_libXfixes_jll"] -git-tree-sha1 = "a376af5c7ae60d29825164db40787f15c80c7c54" -uuid = "a51aa0fd-4e3c-5386-b890-e753decda492" -version = "1.8.3+0" - -[[deps.Xorg_libXinerama_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libXext_jll"] -git-tree-sha1 = "a5bc75478d323358a90dc36766f3c99ba7feb024" -uuid = "d1454406-59df-5ea1-beac-c340f2130bc3" -version = "1.1.6+0" - -[[deps.Xorg_libXrandr_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libXext_jll", "Xorg_libXrender_jll"] -git-tree-sha1 = "aff463c82a773cb86061bce8d53a0d976854923e" -uuid = "ec84b674-ba8e-5d96-8ba1-2a689ba10484" -version = "1.5.5+0" - -[[deps.Xorg_libXrender_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] -git-tree-sha1 = "7ed9347888fac59a618302ee38216dd0379c480d" -uuid = "ea2f1a96-1ddc-540d-b46f-429655e07cfa" -version = "0.9.12+0" - -[[deps.Xorg_libxcb_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libXau_jll", "Xorg_libXdmcp_jll"] -git-tree-sha1 = "bfcaf7ec088eaba362093393fe11aa141fa15422" -uuid = "c7cfdc94-dc32-55de-ac96-5a1b8d977c5b" -version = "1.17.1+0" - -[[deps.Xorg_libxkbfile_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] -git-tree-sha1 = "e3150c7400c41e207012b41659591f083f3ef795" -uuid = "cc61e674-0454-545c-8b26-ed2c68acab7a" -version = "1.1.3+0" - -[[deps.Xorg_xcb_util_cursor_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xcb_util_image_jll", "Xorg_xcb_util_jll", "Xorg_xcb_util_renderutil_jll"] -git-tree-sha1 = "9750dc53819eba4e9a20be42349a6d3b86c7cdf8" -uuid = "e920d4aa-a673-5f3a-b3d7-f755a4d47c43" -version = "0.1.6+0" - -[[deps.Xorg_xcb_util_image_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xcb_util_jll"] -git-tree-sha1 = "f4fc02e384b74418679983a97385644b67e1263b" -uuid = "12413925-8142-5f55-bb0e-6d7ca50bb09b" -version = "0.4.1+0" - -[[deps.Xorg_xcb_util_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll"] -git-tree-sha1 = "68da27247e7d8d8dafd1fcf0c3654ad6506f5f97" -uuid = "2def613f-5ad1-5310-b15b-b15d46f528f5" -version = "0.4.1+0" - -[[deps.Xorg_xcb_util_keysyms_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xcb_util_jll"] -git-tree-sha1 = "44ec54b0e2acd408b0fb361e1e9244c60c9c3dd4" -uuid = "975044d2-76e6-5fbe-bf08-97ce7c6574c7" -version = "0.4.1+0" - -[[deps.Xorg_xcb_util_renderutil_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xcb_util_jll"] -git-tree-sha1 = "5b0263b6d080716a02544c55fdff2c8d7f9a16a0" -uuid = "0d47668e-0667-5a69-a72c-f761630bfb7e" -version = "0.3.10+0" - -[[deps.Xorg_xcb_util_wm_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xcb_util_jll"] -git-tree-sha1 = "f233c83cad1fa0e70b7771e0e21b061a116f2763" -uuid = "c22f9ab0-d5fe-5066-847c-f4bb1cd4e361" -version = "0.4.2+0" - -[[deps.Xorg_xkbcomp_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxkbfile_jll"] -git-tree-sha1 = "801a858fc9fb90c11ffddee1801bb06a738bda9b" -uuid = "35661453-b289-5fab-8a00-3d9160c6a3a4" -version = "1.4.7+0" - -[[deps.Xorg_xkeyboard_config_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xkbcomp_jll"] -git-tree-sha1 = "00af7ebdc563c9217ecc67776d1bbf037dbcebf4" -uuid = "33bec58e-1273-512f-9401-5d533626f822" -version = "2.44.0+0" - -[[deps.Xorg_xtrans_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "a63799ff68005991f9d9491b6e95bd3478d783cb" -uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10" -version = "1.6.0+0" - -[[deps.Zlib_jll]] -deps = ["Libdl"] -uuid = "83775a58-1f1d-513f-b197-d71354ab007a" -version = "1.3.1+2" - -[[deps.Zstd_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "446b23e73536f84e8037f5dce465e92275f6a308" -uuid = "3161d3a3-bdf6-5164-811a-617609db77b4" -version = "1.5.7+1" - -[[deps.eudev_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "c3b0e6196d50eab0c5ed34021aaa0bb463489510" -uuid = "35ca27e7-8b34-5b7f-bca9-bdc33f59eb06" -version = "3.2.14+0" - -[[deps.fzf_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "b6a34e0e0960190ac2a4363a1bd003504772d631" -uuid = "214eeab7-80f7-51ab-84ad-2988db7cef09" -version = "0.61.1+0" - -[[deps.libaom_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "371cc681c00a3ccc3fbc5c0fb91f58ba9bec1ecf" -uuid = "a4ae2306-e953-59d6-aa16-d00cac43593b" -version = "3.13.1+0" - -[[deps.libass_jll]] -deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl", "Zlib_jll"] -git-tree-sha1 = "125eedcb0a4a0bba65b657251ce1d27c8714e9d6" -uuid = "0ac62f75-1d6f-5e53-bd7c-93b484bb37c0" -version = "0.17.4+0" - -[[deps.libblastrampoline_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" -version = "5.15.0+0" - -[[deps.libdecor_jll]] -deps = ["Artifacts", "Dbus_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pango_jll", "Wayland_jll", "xkbcommon_jll"] -git-tree-sha1 = "9bf7903af251d2050b467f76bdbe57ce541f7f4f" -uuid = "1183f4f0-6f2a-5f1a-908b-139f9cdfea6f" -version = "0.2.2+0" - -[[deps.libevdev_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "56d643b57b188d30cccc25e331d416d3d358e557" -uuid = "2db6ffa8-e38f-5e21-84af-90c45d0032cc" -version = "1.13.4+0" - -[[deps.libfdk_aac_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "646634dd19587a56ee2f1199563ec056c5f228df" -uuid = "f638f0a6-7fb0-5443-88ba-1cc74229b280" -version = "2.0.4+0" - -[[deps.libinput_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "eudev_jll", "libevdev_jll", "mtdev_jll"] -git-tree-sha1 = "91d05d7f4a9f67205bd6cf395e488009fe85b499" -uuid = "36db933b-70db-51c0-b978-0f229ee0e533" -version = "1.28.1+0" - -[[deps.libpng_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Zlib_jll"] -git-tree-sha1 = "07b6a107d926093898e82b3b1db657ebe33134ec" -uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f" -version = "1.6.50+0" - -[[deps.libvorbis_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Ogg_jll"] -git-tree-sha1 = "11e1772e7f3cc987e9d3de991dd4f6b2602663a5" -uuid = "f27f6e37-5d2b-51aa-960f-b287f2bc3b7a" -version = "1.3.8+0" - -[[deps.mtdev_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "b4d631fd51f2e9cdd93724ae25b2efc198b059b1" -uuid = "009596ad-96f7-51b1-9f1b-5ce2d5e8a71e" -version = "1.1.7+0" - -[[deps.nghttp2_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" -version = "1.64.0+1" - -[[deps.oneTBB_jll]] -deps = ["Artifacts", "JLLWrappers", "LazyArtifacts", "Libdl"] -git-tree-sha1 = "1350188a69a6e46f799d3945beef36435ed7262f" -uuid = "1317d2d5-d96f-522e-a858-c73665f53c3e" -version = "2022.0.0+1" - -[[deps.p7zip_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" -version = "17.5.0+2" - -[[deps.x264_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "14cc7083fc6dff3cc44f2bc435ee96d06ed79aa7" -uuid = "1270edf5-f2f9-52d2-97e9-ab00b5d0237a" -version = "10164.0.1+0" - -[[deps.x265_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "e7b67590c14d487e734dcb925924c5dc43ec85f3" -uuid = "dfaa095f-4041-5dcd-9319-2fabd8486b76" -version = "4.1.0+0" - -[[deps.xkbcommon_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll", "Xorg_xkeyboard_config_jll"] -git-tree-sha1 = "fbf139bce07a534df0e699dbb5f5cc9346f95cc1" -uuid = "d8fb68d0-12a3-5cfd-a85a-d49703b185fd" -version = "1.9.2+0" diff --git a/Project.toml b/Project.toml deleted file mode 100644 index f7efb7ebea..0000000000 --- a/Project.toml +++ /dev/null @@ -1,4 +0,0 @@ -[deps] -Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" -RDatasets = "ce6b1742-4840-55fa-b093-852dadbb1d8b" -StatsPlots = "f3b207a7-027a-5e70-b257-86293d7955fd" diff --git a/README.md b/README.md index 54d01bb0f1..a538615ec5 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,6 @@ -# engine extension for julia in quarto +# Julia engine extension for quarto -Quarto will introduce engine extensions in 1.9, and this is an experimental port of Quarto's Julia engine to the new architecture. - -Install it with - -``` -quarto add gordonwoodhull/quarto-julia-engine -``` - -You'll need the `feature/engine-extension-3` branch of Quarto, until it's merged. +Quarto's Julia engine, extracted from quarto-cli into a standalone engine extension. It was originally built directly into quarto-cli and is now pulled back in via a git subtree. ## Development diff --git a/example.qmd b/example.qmd deleted file mode 100644 index 449ad1232e..0000000000 --- a/example.qmd +++ /dev/null @@ -1,80 +0,0 @@ ---- -title: "Julia Violin Plots Example" -author: us.anthropic.claude-sonnet-4-5-20250929-v1:0 -format: - html: - code-fold: true - fig-width: 7 - fig-height: 5 - fig-format: svg - fig-responsive: true - html-math-method: katex - code-tools: true -engine: julia ---- - -## Iris Dataset Visualization - -This example demonstrates how to create violin plots with Julia. We'll visualize the famous Iris dataset which contains measurements for three species of iris flowers. - -```{julia} -#| label: fig-violin-grid -# Load required packages -using RDatasets -using Plots -using StatsPlots - -# Load the Iris dataset -iris = dataset("datasets", "iris") - -# Create four violin plots - one for each measurement -p1 = @df iris violin(:Species, :SepalLength, - legend=false, - ylabel="Sepal Length (cm)", - fillalpha=0.7, - title="Sepal Length", - size=(300, 250)) - -p2 = @df iris violin(:Species, :SepalWidth, - legend=false, - ylabel="Sepal Width (cm)", - fillalpha=0.7, - color=:green, - title="Sepal Width", - size=(300, 250)) - -p3 = @df iris violin(:Species, :PetalLength, - legend=false, - ylabel="Petal Length (cm)", - fillalpha=0.7, - color=:orange, - title="Petal Length", - size=(300, 250)) - -p4 = @df iris violin(:Species, :PetalWidth, - legend=false, - ylabel="Petal Width (cm)", - fillalpha=0.7, - color=:red, - title="Petal Width", - size=(300, 250)) - -plot(p1, p2, p3, p4, - layout=(2,2), - size=(800, 800), - titlefontsize=10, - guidefontsize=9, - tickfontsize=7) -``` - -::: {.callout-note} -## What is a violin plot? - -A violin plot shows the distribution of data across categories. The width of each "violin" represents the density of data points at that value, making it easy to compare distributions between groups. - -In this example, we can see that: - -- *Setosa* has the shortest petals but widest sepals -- *Virginica* has the longest petals and sepals -- *Versicolor* falls between the other two species for most measurements -::: \ No newline at end of file From 3878ec187bbd48e99a5dfb1f440b601933a076d1 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Thu, 19 Mar 2026 11:38:10 +0100 Subject: [PATCH 2/6] Squashed 'src/resources/extension-subtrees/julia-engine/' changes from 605d04dfd..f591fc453 f591fc453 Merge pull request #6 from PumasAI/jk/bundle-julia-resources 16a1aa0f4 chore: gitignore .claude directory 037c7bca4 docs: simplify test structure description in AGENTS.md f32744043 ci: make quarto-cli repo configurable 134aae7e0 docs: update AGENTS.md with testing and CI documentation baec85652 ci: bump actions/cache to v5.0.4, add actions:write permission 0869d5475 ci: cache typst-gather Cargo build across runs 80ae1c460 chore: whitelist only source files in test docs directory e3558bde0 ci: cancel in-progress runs on new pushes 50a9150c7 chore: remove render-generated .gitignore 0686c80e7 refactor: flatten docs/smoke-all into docs/julia-engine 0c83c6f26 refactor: restructure tests for easy quarto-cli integration 43952eb68 ci: add macOS and Windows test runners bbcae8fb4 refactor: always use quarto's bundled deno in test runner 003400e13 feat: add run-tests.sh wrapper, use quarto's bundled deno 25d0a3b81 chore: remove render artifacts and gitignore test outputs 5928ee7c7 feat: move Julia engine tests into extension repo f0791c8d1 ci: detect Julia version from quarto-cli test Manifest 5a67989d1 ci: bump quarto-cli rev to v1.9.35 4d564d55c ci: add GitHub Actions workflow for Julia engine tests 3c52cbe8f feat: bundle Julia resource files into the extension git-subtree-dir: src/resources/extension-subtrees/julia-engine git-subtree-split: f591fc4536f7f98c7be8f8cd128f85059dc24145 --- .github/workflows/ci.yml | 100 ++++++++ .gitignore | 2 + AGENTS.md | 41 +++- _extensions/julia-engine/Project.toml | 5 + .../julia-engine/ensure_environment.jl | 32 +++ _extensions/julia-engine/julia-engine.js | 157 +++++++++++- .../julia-engine/quartonotebookrunner.jl | 57 +++++ .../start_quartonotebookrunner_detached.jl | 17 ++ src/julia-engine.ts | 21 +- tests/docs/julia-engine/.gitignore | 6 + .../engine-reordering/_quarto.yml | 3 + .../engine-reordering/notebook.qmd | 10 + tests/docs/julia-engine/sleep/_quarto.yml | 2 + tests/docs/julia-engine/sleep/sleep.qmd | 9 + .../julia-engine/source-ranges/_included.qmd | 3 + .../source-ranges/source-ranges-test.qmd | 11 + tests/run-tests.ps1 | 31 +++ tests/run-tests.sh | 37 +++ tests/smoke/julia-engine/julia.test.ts | 227 ++++++++++++++++++ tests/smoke/julia-engine/render.test.ts | 55 +++++ 20 files changed, 797 insertions(+), 29 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 _extensions/julia-engine/Project.toml create mode 100644 _extensions/julia-engine/ensure_environment.jl create mode 100644 _extensions/julia-engine/quartonotebookrunner.jl create mode 100644 _extensions/julia-engine/start_quartonotebookrunner_detached.jl create mode 100644 tests/docs/julia-engine/.gitignore create mode 100644 tests/docs/julia-engine/engine-reordering/_quarto.yml create mode 100644 tests/docs/julia-engine/engine-reordering/notebook.qmd create mode 100644 tests/docs/julia-engine/sleep/_quarto.yml create mode 100644 tests/docs/julia-engine/sleep/sleep.qmd create mode 100644 tests/docs/julia-engine/source-ranges/_included.qmd create mode 100644 tests/docs/julia-engine/source-ranges/source-ranges-test.qmd create mode 100644 tests/run-tests.ps1 create mode 100755 tests/run-tests.sh create mode 100644 tests/smoke/julia-engine/julia.test.ts create mode 100644 tests/smoke/julia-engine/render.test.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000000..4587605e71 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,100 @@ +name: CI +on: + push: + branches: [main] + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + actions: write + contents: read + +# Reference quarto-cli version to test against. +# Full commit hash for reproducibility; comment documents the human-readable version. +env: + QUARTO_CLI_REPO: quarto-dev/quarto-cli + QUARTO_CLI_REV: 97e7649bf14607cf39cda13f013185a4146e047b # v1.9.35 + +jobs: + test: + name: Julia engine tests (${{ matrix.os }}) + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Checkout quarto-cli + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + repository: ${{ env.QUARTO_CLI_REPO }} + ref: ${{ env.QUARTO_CLI_REV }} + path: _quarto-cli + + - name: Cache typst-gather build + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + with: + path: _quarto-cli/package/typst-gather/target + key: ${{ runner.os }}-typst-gather-${{ env.QUARTO_CLI_REV }} + + - name: Configure Quarto (Unix) + if: runner.os != 'Windows' + working-directory: _quarto-cli + run: ./configure.sh + + - name: Configure Quarto (Windows) + if: runner.os == 'Windows' + working-directory: _quarto-cli + shell: cmd + run: ./configure.cmd + + - name: Add quarto to PATH (Windows) + if: runner.os == 'Windows' + shell: pwsh + run: | + "$(Get-ChildItem -Path _quarto-cli/package/dist/bin/quarto.cmd | ForEach-Object { $_.FullName } | Split-Path)" >> $env:GITHUB_PATH + + # Rebuild the bundled JS from our TS source using the configured quarto, + # then verify it matches what's checked in. This catches forgotten rebuilds. + - name: Verify bundled JS is up to date (Unix) + if: runner.os != 'Windows' + run: | + quarto call build-ts-extension src/julia-engine.ts + if ! git diff --exit-code _extensions/julia-engine/julia-engine.js; then + echo "::error::Bundled JS is out of date. Run 'quarto call build-ts-extension src/julia-engine.ts' and commit the result." + exit 1 + fi + + - name: Verify bundled JS is up to date (Windows) + if: runner.os == 'Windows' + shell: pwsh + run: | + quarto call build-ts-extension src/julia-engine.ts + git diff --exit-code _extensions/julia-engine/julia-engine.js + if ($LASTEXITCODE -ne 0) { + Write-Error "Bundled JS is out of date. Run 'quarto call build-ts-extension src/julia-engine.ts' and commit the result." + exit 1 + } + + - name: Setup Julia + uses: julia-actions/setup-julia@5c9647d97b78a5debe5164e9eec09d653d29bd71 # v2.6.1 + with: + version: "1.11" + + - name: Cache Julia packages + uses: julia-actions/cache@b6a98a496542b2eeda9b4402ce8f79f8fd753d5e # v3.0.0 + + - name: Run tests (Unix) + if: runner.os != 'Windows' + run: tests/run-tests.sh + + - name: Run tests (Windows) + if: runner.os == 'Windows' + shell: pwsh + run: tests/run-tests.ps1 diff --git a/.gitignore b/.gitignore index 6d3ece8daa..f54fb86607 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ .DS_Store +.claude/ .quarto/ **/*.quarto_ipynb +scratch/ diff --git a/AGENTS.md b/AGENTS.md index 9f5824e830..4c7e6d7010 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -6,9 +6,10 @@ The engine extension requires a compatible version of quarto (>= 1.9.0). If work ## Repo structure -- `_extensions/julia-engine/` — the actual extension (contains `_extension.yml` and the bundled `julia-engine.js`) +- `_extensions/julia-engine/` — the actual extension (contains `_extension.yml`, the bundled `julia-engine.js`, and Julia resource files like `Project.toml`, `*.jl`) - `src/` — TypeScript source for the engine (`julia-engine.ts`, `constants.ts`). Changes here must be bundled into `_extensions/julia-engine/julia-engine.js` to take effect. - `_quarto.yml` — makes the repo root a quarto project so rendering picks up the extension from `_extensions/` +- `tests/` — self-contained test suite (see Testing below) ## Building @@ -18,27 +19,43 @@ After editing the TypeScript source in `src/`, rebuild the bundled JS: quarto call build-ts-extension src/julia-engine.ts ``` -This bundles `src/julia-engine.ts` into `_extensions/julia-engine/julia-engine.js`. +This bundles `src/julia-engine.ts` into `_extensions/julia-engine/julia-engine.js`. CI verifies the bundled JS matches the TS source. -## Testing locally +## Testing + +Tests are self-contained Deno tests using `jsr:` imports — no import map or quarto internals needed. `tests/docs/` contains `.qmd` files and quarto projects that the `.test.ts` files in `tests/smoke/` render and verify. These directories mirror quarto-cli's `tests/docs/` and `tests/smoke/` structure and are intended to be merged into those directories when quarto-cli runs its own CI (since this extension is a fixed part of quarto-cli via git subtree). + +### Running tests locally + +The test runner uses the deno bundled with quarto (to avoid version mismatches): + +```sh +# With quarto on PATH: +tests/run-tests.sh + +# With explicit quarto path: +QUARTO=/path/to/quarto tests/run-tests.sh + +# Run a specific test file: +tests/run-tests.sh smoke/julia-engine/render.test.ts +``` ### Quick: render in this repo -Create or edit a `.qmd` file in the repo root with `engine: julia` and render it: +Create or edit a `.qmd` file in the repo root (e.g. in `scratch/`) with `engine: julia` and render it: ```sh -quarto render some-file.qmd +quarto render scratch/test.qmd ``` Since this directory is a quarto project with the extension in `_extensions/`, quarto discovers and uses the engine from here. -### Full: run quarto-cli's tests +## CI -To test with quarto-cli's own test suite, push the current state into the subtree in a local quarto-cli clone: +CI runs on all three platforms (Linux, macOS, Windows) against a pinned quarto-cli revision (see `QUARTO_CLI_REV` in `.github/workflows/ci.yml`). It: -```sh -cd /path/to/quarto-cli -git subtree pull --prefix=src/resources/extension-subtrees/julia-engine /path/to/quarto-julia-engine --squash -``` +1. Configures quarto from the pinned rev +2. Verifies the bundled JS is up to date with the TS source +3. Runs the full test suite -Then run quarto-cli's tests as usual. +When bumping `QUARTO_CLI_REV`, use the full commit hash annotated with the version tag for clarity (e.g. `abc123 # v1.9.35`). diff --git a/_extensions/julia-engine/Project.toml b/_extensions/julia-engine/Project.toml new file mode 100644 index 0000000000..94def5bfc2 --- /dev/null +++ b/_extensions/julia-engine/Project.toml @@ -0,0 +1,5 @@ +[deps] +QuartoNotebookRunner = "4c0109c6-14e9-4c88-93f0-2b974d3468f4" + +[compat] +QuartoNotebookRunner = "=0.17.4" diff --git a/_extensions/julia-engine/ensure_environment.jl b/_extensions/julia-engine/ensure_environment.jl new file mode 100644 index 0000000000..3274a230f8 --- /dev/null +++ b/_extensions/julia-engine/ensure_environment.jl @@ -0,0 +1,32 @@ +using Pkg +using TOML + +# If the manifest was resolved with the exact Project.toml that we have copied +# over from quarto's resource folder, then we just ensure that it is instantiated, +# all packages are downloaded correctly, etc. +# Otherwise, we update to get the newest packages fulfilling the compat bounds set in +# the Project.toml. + +function manifest_has_correct_julia_version() + project_file = Base.active_project() + manifest_file = joinpath(dirname(project_file), "Manifest.toml") + version = VersionNumber(TOML.parsefile(manifest_file)["julia_version"]) + return version.major == VERSION.major && version.minor == VERSION.minor +end + +is_manifest_current = @static if VERSION < v"1.11.0-DEV.1135" + Pkg.is_manifest_current() +else + Pkg.is_manifest_current(dirname(Base.active_project())) +end + +manifest_matches_project_toml = is_manifest_current === true # this returns nothing if there's no manifest + +if manifest_matches_project_toml && manifest_has_correct_julia_version() + Pkg.instantiate() +else + Pkg.update() +end +# not strictly necessary, but in case of precompilation errors this will +# actually print them out explicitly +Pkg.precompile() diff --git a/_extensions/julia-engine/julia-engine.js b/_extensions/julia-engine/julia-engine.js index 04c4f0a11d..d3a33c0f9a 100644 --- a/_extensions/julia-engine/julia-engine.js +++ b/_extensions/julia-engine/julia-engine.js @@ -8,6 +8,22 @@ function assertPath(path) { } } +// deno:https://jsr.io/@std/path/1.0.8/_common/strip_trailing_separators.ts +function stripTrailingSeparators(segment, isSep) { + if (segment.length <= 1) { + return segment; + } + let end = segment.length; + for (let i = segment.length - 1; i > 0; i--) { + if (isSep(segment.charCodeAt(i))) { + end = i; + } else { + break; + } + } + return segment.slice(0, end); +} + // deno:https://jsr.io/@std/path/1.0.8/_common/constants.ts var CHAR_UPPERCASE_A = 65; var CHAR_LOWERCASE_A = 97; @@ -24,6 +40,9 @@ function isPosixPathSeparator(code) { } // deno:https://jsr.io/@std/path/1.0.8/windows/_util.ts +function isPosixPathSeparator2(code) { + return code === CHAR_FORWARD_SLASH; +} function isPathSeparator(code) { return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH; } @@ -31,6 +50,133 @@ function isWindowsDeviceRoot(code) { return code >= CHAR_LOWERCASE_A && code <= CHAR_LOWERCASE_Z || code >= CHAR_UPPERCASE_A && code <= CHAR_UPPERCASE_Z; } +// deno:https://jsr.io/@std/path/1.0.8/_common/dirname.ts +function assertArg(path) { + assertPath(path); + if (path.length === 0) return "."; +} + +// deno:https://jsr.io/@std/path/1.0.8/posix/dirname.ts +function dirname(path) { + assertArg(path); + let end = -1; + let matchedNonSeparator = false; + for (let i = path.length - 1; i >= 1; --i) { + if (isPosixPathSeparator(path.charCodeAt(i))) { + if (matchedNonSeparator) { + end = i; + break; + } + } else { + matchedNonSeparator = true; + } + } + if (end === -1) { + return isPosixPathSeparator(path.charCodeAt(0)) ? "/" : "."; + } + return stripTrailingSeparators(path.slice(0, end), isPosixPathSeparator); +} + +// deno:https://jsr.io/@std/path/1.0.8/windows/dirname.ts +function dirname2(path) { + assertArg(path); + const len = path.length; + let rootEnd = -1; + let end = -1; + let matchedSlash = true; + let offset = 0; + const code = path.charCodeAt(0); + if (len > 1) { + if (isPathSeparator(code)) { + rootEnd = offset = 1; + if (isPathSeparator(path.charCodeAt(1))) { + let j = 2; + let last = j; + for (; j < len; ++j) { + if (isPathSeparator(path.charCodeAt(j))) break; + } + if (j < len && j !== last) { + last = j; + for (; j < len; ++j) { + if (!isPathSeparator(path.charCodeAt(j))) break; + } + if (j < len && j !== last) { + last = j; + for (; j < len; ++j) { + if (isPathSeparator(path.charCodeAt(j))) break; + } + if (j === len) { + return path; + } + if (j !== last) { + rootEnd = offset = j + 1; + } + } + } + } + } else if (isWindowsDeviceRoot(code)) { + if (path.charCodeAt(1) === CHAR_COLON) { + rootEnd = offset = 2; + if (len > 2) { + if (isPathSeparator(path.charCodeAt(2))) rootEnd = offset = 3; + } + } + } + } else if (isPathSeparator(code)) { + return path; + } + for (let i = len - 1; i >= offset; --i) { + if (isPathSeparator(path.charCodeAt(i))) { + if (!matchedSlash) { + end = i; + break; + } + } else { + matchedSlash = false; + } + } + if (end === -1) { + if (rootEnd === -1) return "."; + else end = rootEnd; + } + return stripTrailingSeparators(path.slice(0, end), isPosixPathSeparator2); +} + +// deno:https://jsr.io/@std/path/1.0.8/dirname.ts +function dirname3(path) { + return isWindows ? dirname2(path) : dirname(path); +} + +// deno:https://jsr.io/@std/path/1.0.8/_common/from_file_url.ts +function assertArg3(url) { + url = url instanceof URL ? url : new URL(url); + if (url.protocol !== "file:") { + throw new TypeError(`URL must be a file URL: received "${url.protocol}"`); + } + return url; +} + +// deno:https://jsr.io/@std/path/1.0.8/posix/from_file_url.ts +function fromFileUrl(url) { + url = assertArg3(url); + return decodeURIComponent(url.pathname.replace(/%(?![0-9A-Fa-f]{2})/g, "%25")); +} + +// deno:https://jsr.io/@std/path/1.0.8/windows/from_file_url.ts +function fromFileUrl2(url) { + url = assertArg3(url); + let path = decodeURIComponent(url.pathname.replace(/\//g, "\\").replace(/%(?![0-9A-Fa-f]{2})/g, "%25")).replace(/^\\*([A-Za-z]:)(\\|$)/, "$1\\"); + if (url.hostname !== "") { + path = `\\\\${url.hostname}${path}`; + } + return path; +} + +// deno:https://jsr.io/@std/path/1.0.8/from_file_url.ts +function fromFileUrl3(url) { + return isWindows ? fromFileUrl2(url) : fromFileUrl(url); +} + // deno:https://jsr.io/@std/path/1.0.8/_common/normalize.ts function assertArg4(path) { assertPath(path); @@ -488,6 +634,7 @@ var kKeepHidden = "keep-hidden"; // src/julia-engine.ts var isWindows2 = Deno.build.os === "windows"; +var extensionDir = dirname3(fromFileUrl3(import.meta.url)); var quarto; function safeRemoveSync(file, options = {}) { try { @@ -716,7 +863,7 @@ async function startOrReuseJuliaServer(options) { "Start-Process", juliaCmd(), "-ArgumentList", - powershell_argument_list_to_string("--startup-file=no", `--project=${juliaProject}`, quarto.path.resource("julia", "quartonotebookrunner.jl"), transportFile, juliaServerLogFile()), + powershell_argument_list_to_string("--startup-file=no", `--project=${juliaProject}`, join3(extensionDir, "quartonotebookrunner.jl"), transportFile, juliaServerLogFile()), "-WindowStyle", "Hidden" ], @@ -733,10 +880,10 @@ async function startOrReuseJuliaServer(options) { const command = new Deno.Command(juliaCmd(), { args: [ "--startup-file=no", - quarto.path.resource("julia", "start_quartonotebookrunner_detached.jl"), + join3(extensionDir, "start_quartonotebookrunner_detached.jl"), juliaCmd(), juliaProject, - quarto.path.resource("julia", "quartonotebookrunner.jl"), + join3(extensionDir, "quartonotebookrunner.jl"), transportFile, juliaServerLogFile() ], @@ -762,14 +909,14 @@ async function startOrReuseJuliaServer(options) { } async function ensureQuartoNotebookRunnerEnvironment(options) { const runtimeDir = quarto.path.runtime("julia"); - const projectTomlTemplate = quarto.path.resource("julia", "Project.toml"); + const projectTomlTemplate = join3(extensionDir, "Project.toml"); const projectToml = join3(runtimeDir, "Project.toml"); Deno.writeFileSync(projectToml, Deno.readFileSync(projectTomlTemplate)); const command = new Deno.Command(juliaCmd(), { args: [ "--startup-file=no", `--project=${runtimeDir}`, - quarto.path.resource("julia", "ensure_environment.jl") + join3(extensionDir, "ensure_environment.jl") ] }); const proc = command.spawn(); diff --git a/_extensions/julia-engine/quartonotebookrunner.jl b/_extensions/julia-engine/quartonotebookrunner.jl new file mode 100644 index 0000000000..d2a394da98 --- /dev/null +++ b/_extensions/julia-engine/quartonotebookrunner.jl @@ -0,0 +1,57 @@ +logfile = ARGS[2] +filehandle = open(logfile, "w") + +# We cannot start Julia in a way such that it uses line buffering or no buffering (see https://github.com/JuliaLang/julia/issues/13050) +# which means that if we just redirect all its output into a logfile (for example with `pipeline(julia_cmd, stdout = logfile)`) +# it will not actually write to the logfile until the buffer is filled or it's explicitly flushed, which can take a while. +# So when we check the logfile, we don't actually see anything if the buffer is not filled first, which +# may never be if there's little output. So instead we redirect stdout and stderr to a pipe which we manually check +# for available data in shorter intervals. We then write the data to the logfile and flush. + +pipe = Pipe() +pipe_with_color = IOContext(pipe, :color => Base.get_have_color()) +redirect_stdout(pipe_with_color) +redirect_stderr(pipe_with_color) + +function update_logfile() + data = readavailable(pipe) + if !isempty(data) + write(filehandle, data) + flush(filehandle) + end + return +end + +@async while true + update_logfile() + sleep(1) +end + +# we might lose printout from crashes if we don't do another update at the end +atexit() do + update_logfile() +end + +using Dates: now +@info "Log started at $(now())" + +using QuartoNotebookRunner +using Sockets + +transport_file = ARGS[1] +transport_dir = dirname(transport_file) + +atexit() do + rm(transport_file; force=true) +end + +server = QuartoNotebookRunner.serve(; timeout = 300) +port = server.port + +open(transport_file, "w") do io + println(io, """{"port": $port, "pid": $(Base.Libc.getpid()), "key": "$(server.key)"}""") +end + +@info "Starting server at $(now())" +wait(server) +@info "Server stopped at $(now())" diff --git a/_extensions/julia-engine/start_quartonotebookrunner_detached.jl b/_extensions/julia-engine/start_quartonotebookrunner_detached.jl new file mode 100644 index 0000000000..3771ca5232 --- /dev/null +++ b/_extensions/julia-engine/start_quartonotebookrunner_detached.jl @@ -0,0 +1,17 @@ +# it appears that deno cannot launch detached processes https://github.com/denoland/deno/issues/5501 +# so we use an indirection where we start the detached julia process using julia itself +julia_bin = ARGS[1] +project = ARGS[2] +julia_file = ARGS[3] +transport_file = ARGS[4] +logfile = ARGS[5] + +if length(ARGS) > 5 + error("Too many arguments") +end + +env = copy(ENV) +env["JULIA_LOAD_PATH"] = "@:@stdlib" # ignore the main env +cmd = `$julia_bin --startup-file=no --project=$project $julia_file $transport_file $logfile` +cmd = setenv(cmd, env) +run(detach(cmd), wait = false) diff --git a/src/julia-engine.ts b/src/julia-engine.ts index df2b164dcb..233ca1dd86 100644 --- a/src/julia-engine.ts +++ b/src/julia-engine.ts @@ -5,7 +5,7 @@ */ // Standard library imports -import { join, resolve } from "path"; +import { dirname, fromFileUrl, join, resolve } from "path"; import { existsSync } from "fs/exists"; import { encodeBase64 } from "encoding/base64"; @@ -42,6 +42,9 @@ import { // Platform detection const isWindows = Deno.build.os === "windows"; +// Extension directory (where the bundled JS and resource files live) +const extensionDir = dirname(fromFileUrl(import.meta.url)); + // Module-level quarto API reference let quarto: QuartoAPI; @@ -390,7 +393,7 @@ async function startOrReuseJuliaServer( powershell_argument_list_to_string( "--startup-file=no", `--project=${juliaProject}`, - quarto.path.resource("julia", "quartonotebookrunner.jl"), + join(extensionDir, "quartonotebookrunner.jl"), transportFile, juliaServerLogFile(), ), @@ -414,13 +417,10 @@ async function startOrReuseJuliaServer( const command = new Deno.Command(juliaCmd(), { args: [ "--startup-file=no", - quarto.path.resource( - "julia", - "start_quartonotebookrunner_detached.jl", - ), + join(extensionDir, "start_quartonotebookrunner_detached.jl"), juliaCmd(), juliaProject, - quarto.path.resource("julia", "quartonotebookrunner.jl"), + join(extensionDir, "quartonotebookrunner.jl"), transportFile, juliaServerLogFile(), ], @@ -451,17 +451,14 @@ async function ensureQuartoNotebookRunnerEnvironment( options: JuliaExecuteOptions, ) { const runtimeDir = quarto.path.runtime("julia"); - const projectTomlTemplate = quarto.path.resource( - "julia", - "Project.toml", - ); + const projectTomlTemplate = join(extensionDir, "Project.toml"); const projectToml = join(runtimeDir, "Project.toml"); Deno.writeFileSync(projectToml, Deno.readFileSync(projectTomlTemplate)); const command = new Deno.Command(juliaCmd(), { args: [ "--startup-file=no", `--project=${runtimeDir}`, - quarto.path.resource("julia", "ensure_environment.jl"), + join(extensionDir, "ensure_environment.jl"), ], }); const proc = command.spawn(); diff --git a/tests/docs/julia-engine/.gitignore b/tests/docs/julia-engine/.gitignore new file mode 100644 index 0000000000..351aa45ca5 --- /dev/null +++ b/tests/docs/julia-engine/.gitignore @@ -0,0 +1,6 @@ +# Ignore everything in subdirectories (render artifacts, caches, etc.) +*/* + +# Allow only source files +!*/*.qmd +!*/_quarto.yml diff --git a/tests/docs/julia-engine/engine-reordering/_quarto.yml b/tests/docs/julia-engine/engine-reordering/_quarto.yml new file mode 100644 index 0000000000..3aaa4272ac --- /dev/null +++ b/tests/docs/julia-engine/engine-reordering/_quarto.yml @@ -0,0 +1,3 @@ +project: + type: default +engines: ["julia"] diff --git a/tests/docs/julia-engine/engine-reordering/notebook.qmd b/tests/docs/julia-engine/engine-reordering/notebook.qmd new file mode 100644 index 0000000000..d3cd5f9644 --- /dev/null +++ b/tests/docs/julia-engine/engine-reordering/notebook.qmd @@ -0,0 +1,10 @@ +```{julia} +using Test +@test haskey( + Base.loaded_modules, + Base.PkgId( + Base.UUID("38328d9c-a911-4051-bc06-3f7f556ffeda"), + "QuartoNotebookWorker", + ) +) +``` diff --git a/tests/docs/julia-engine/sleep/_quarto.yml b/tests/docs/julia-engine/sleep/_quarto.yml new file mode 100644 index 0000000000..b8bae5830f --- /dev/null +++ b/tests/docs/julia-engine/sleep/_quarto.yml @@ -0,0 +1,2 @@ +project: + type: default diff --git a/tests/docs/julia-engine/sleep/sleep.qmd b/tests/docs/julia-engine/sleep/sleep.qmd new file mode 100644 index 0000000000..6e8011d326 --- /dev/null +++ b/tests/docs/julia-engine/sleep/sleep.qmd @@ -0,0 +1,9 @@ +--- +engine: julia +params: + sleep_duration: 0 +--- + +```{julia} +sleep(sleep_duration) +``` \ No newline at end of file diff --git a/tests/docs/julia-engine/source-ranges/_included.qmd b/tests/docs/julia-engine/source-ranges/_included.qmd new file mode 100644 index 0000000000..bb2b3e351a --- /dev/null +++ b/tests/docs/julia-engine/source-ranges/_included.qmd @@ -0,0 +1,3 @@ +```{julia} +"$(@__FILE__):$(@__LINE__)" +``` diff --git a/tests/docs/julia-engine/source-ranges/source-ranges-test.qmd b/tests/docs/julia-engine/source-ranges/source-ranges-test.qmd new file mode 100644 index 0000000000..2f59838a9a --- /dev/null +++ b/tests/docs/julia-engine/source-ranges/source-ranges-test.qmd @@ -0,0 +1,11 @@ +--- +title: "Test Julia source ranges with includes" +format: markdown +engine: julia +--- + +{{< include _included.qmd >}} + +```{julia} +"$(@__FILE__):$(@__LINE__)" +``` diff --git a/tests/run-tests.ps1 b/tests/run-tests.ps1 new file mode 100644 index 0000000000..225f8d1628 --- /dev/null +++ b/tests/run-tests.ps1 @@ -0,0 +1,31 @@ +# Always use the deno bundled with quarto to avoid version mismatches. +# Accepts QUARTO env var or falls back to quarto on PATH. +$ErrorActionPreference = "Stop" + +$quartoCmd = if ($env:QUARTO) { $env:QUARTO } else { "quarto" } + +$quartoBin = & $quartoCmd --paths | Select-Object -First 1 +$deno = Join-Path $quartoBin "tools" "x86_64" "deno.exe" + +if (-not (Test-Path $deno)) { + # Try aarch64 if x86_64 not found + $deno = Join-Path $quartoBin "tools" "aarch64" "deno.exe" +} + +if (-not (Test-Path $deno)) { + Write-Error "deno not found in quarto tools directory" + exit 1 +} + +Push-Location $PSScriptRoot +try { + # Run explicit files if given, otherwise discover all .test.ts files + if ($args.Count -gt 0) { + & $deno test --allow-all --no-check @args + } else { + & $deno test --allow-all --no-check smoke/ + } + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } +} finally { + Pop-Location +} diff --git a/tests/run-tests.sh b/tests/run-tests.sh new file mode 100755 index 0000000000..6791e8e97a --- /dev/null +++ b/tests/run-tests.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Always use the deno bundled with quarto to avoid version mismatches. +# Accepts QUARTO env var or falls back to quarto on PATH. +QUARTO="${QUARTO:-quarto}" + +if ! command -v "$QUARTO" &>/dev/null; then + echo "quarto not found. Either set QUARTO=/path/to/quarto or add it to PATH." >&2 + exit 1 +fi + +QUARTO_BIN_DIR="$("$QUARTO" --paths | head -1)" + +case "$(uname -m)" in + x86_64) DENO_ARCH_DIR=x86_64 ;; + aarch64|arm64) DENO_ARCH_DIR=aarch64 ;; + *) echo "Unsupported architecture: $(uname -m)" >&2; exit 1 ;; +esac + +DENO="$QUARTO_BIN_DIR/tools/$DENO_ARCH_DIR/deno" + +if [ ! -x "$DENO" ]; then + echo "deno not found at $DENO" >&2 + exit 1 +fi + +cd "$SCRIPT_DIR" + +# Run explicit files if given, otherwise discover all .test.ts files +if [ $# -gt 0 ]; then + "$DENO" test --allow-all --no-check "$@" +else + "$DENO" test --allow-all --no-check smoke/ +fi diff --git a/tests/smoke/julia-engine/julia.test.ts b/tests/smoke/julia-engine/julia.test.ts new file mode 100644 index 0000000000..ecf33cc68f --- /dev/null +++ b/tests/smoke/julia-engine/julia.test.ts @@ -0,0 +1,227 @@ +import { assert, assertStringIncludes } from "jsr:@std/assert"; +import { existsSync } from "jsr:@std/fs/exists"; +import { join } from "jsr:@std/path"; + +const isWindows = Deno.build.os === "windows"; +function quartoCmd(): string { + return isWindows ? "quarto.cmd" : "quarto"; +} +function docs(path: string): string { + return join("docs", path); +} +const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); + +// Resolve the quarto runtime dir for Julia (where transport/log files live). +// Mirrors quarto's quartoRuntimeDir("julia"). +function quartoRuntimeDir(): string { + let base: string; + switch (Deno.build.os) { + case "darwin": + base = join(Deno.env.get("HOME")!, "Library", "Caches", "quarto"); + break; + case "windows": + base = join(Deno.env.get("LOCALAPPDATA")!, "quarto"); + break; + default: { + const xdgRuntime = Deno.env.get("XDG_RUNTIME_DIR"); + if (xdgRuntime) { + base = join(xdgRuntime, "quarto"); + } else { + const cacheHome = Deno.env.get("XDG_CACHE_HOME") ?? join(Deno.env.get("HOME")!, ".cache"); + base = join(cacheHome, "quarto"); + } + break; + } + } + return join(base, "julia"); +} + +const juliaTestDir = docs("julia-engine/sleep"); +const sleepQmd = "sleep.qmd"; +assert(existsSync(join(juliaTestDir, sleepQmd))); + +function assertSuccess(output: Deno.CommandOutput) { + if (!output.success) { + console.error("Command failed:"); + console.error("stdout:\n" + new TextDecoder().decode(output.stdout)); + console.error("stderr:\n" + new TextDecoder().decode(output.stderr)); + throw new Error("Command execution was not successful"); + } +} + +function assertStdoutIncludes(output: Deno.CommandOutput, str: string) { + assertStringIncludes(new TextDecoder().decode(output.stdout), str); +} +function assertStderrIncludes(output: Deno.CommandOutput, str: string) { + assertStringIncludes(new TextDecoder().decode(output.stderr), str); +} + +Deno.test("julia engine", async (t) => { + // Setup: Clean up any leftover server state before running tests + const killcmd = new Deno.Command( + quartoCmd(), + { args: ["call", "engine", "julia", "kill"], cwd: juliaTestDir }, + ).outputSync(); + assertSuccess(killcmd); + + const transportFile = join(quartoRuntimeDir(), "julia_transport.txt"); + const logFile = join(quartoRuntimeDir(), "julia_server_log.txt"); + + try { + await Deno.remove(transportFile); + } catch { + // File might not exist, that's okay + } + + try { + await Deno.remove(logFile); + } catch { + // File might not exist, that's okay + } + + await t.step("kill without server running", () => { + const output = new Deno.Command( + quartoCmd(), + { args: ["call", "engine", "julia", "kill"], cwd: juliaTestDir }, + ).outputSync(); + assertSuccess(output); + assertStderrIncludes(output, "Julia control server is not running."); + }); + + await t.step("status without server running", () => { + const output = new Deno.Command( + quartoCmd(), + { args: ["call", "engine", "julia", "status"], cwd: juliaTestDir }, + ).outputSync(); + assertSuccess(output); + assertStderrIncludes(output, "Julia control server is not running."); + }); + + await t.step("log file doesn't exist", () => { + const log_output = new Deno.Command( + quartoCmd(), + { args: ["call", "engine", "julia", "log"], cwd: juliaTestDir }, + ).outputSync(); + assertSuccess(log_output); + assertStderrIncludes(log_output, "Server log file doesn't exist"); + }); + + await t.step("status with server and worker running", () => { + const render_output = new Deno.Command( + quartoCmd(), + { + args: [ + "render", + sleepQmd, + "-P", + "sleep_duration:0", + "--execute-daemon", + "60", + ], + cwd: juliaTestDir, + }, + ).outputSync(); + assertSuccess(render_output); + + const status_output = new Deno.Command( + quartoCmd(), + { args: ["call", "engine", "julia", "status"], cwd: juliaTestDir }, + ).outputSync(); + assertSuccess(status_output); + assertStdoutIncludes(status_output, "workers active: 1"); + }); + + await t.step("closing an idling worker", () => { + const close_output = new Deno.Command( + quartoCmd(), + { + args: ["call", "engine", "julia", "close", sleepQmd], + cwd: juliaTestDir, + }, + ).outputSync(); + assertSuccess(close_output); + assertStderrIncludes(close_output, "Worker closed successfully"); + + const status_output = new Deno.Command( + quartoCmd(), + { args: ["call", "engine", "julia", "status"], cwd: juliaTestDir }, + ).outputSync(); + assertSuccess(status_output); + assertStdoutIncludes(status_output, "workers active: 0"); + }); + + await t.step("force-closing a running worker", async () => { + const render_cmd = new Deno.Command( + quartoCmd(), + { + args: ["render", sleepQmd, "-P", "sleep_duration:30"], + cwd: juliaTestDir, + }, + ).output(); + + await delay(3000); + + const close_output = new Deno.Command( + quartoCmd(), + { + args: ["call", "engine", "julia", "close", sleepQmd], + cwd: juliaTestDir, + }, + ).outputSync(); + assertStderrIncludes(close_output, "worker is busy"); + + const status_output = new Deno.Command( + quartoCmd(), + { args: ["call", "engine", "julia", "status"], cwd: juliaTestDir }, + ).outputSync(); + assertSuccess(status_output); + assertStdoutIncludes(status_output, "workers active: 1"); + + const force_close_output = new Deno.Command( + quartoCmd(), + { + args: ["call", "engine", "julia", "close", "--force", sleepQmd], + cwd: juliaTestDir, + }, + ).outputSync(); + assertSuccess(force_close_output); + assertStderrIncludes(force_close_output, "Worker force-closed successfully"); + + const status_output_2 = new Deno.Command( + quartoCmd(), + { args: ["call", "engine", "julia", "status"], cwd: juliaTestDir }, + ).outputSync(); + assertSuccess(status_output_2); + assertStdoutIncludes(status_output_2, "workers active: 0"); + + const render_output = await render_cmd; + assertStderrIncludes(render_output, "File was force-closed during run"); + }); + + await t.step("log exists", () => { + const log_output = new Deno.Command( + quartoCmd(), + { args: ["call", "engine", "julia", "log"], cwd: juliaTestDir }, + ).outputSync(); + assertSuccess(log_output); + assertStdoutIncludes(log_output, "Log started at"); + }); + + await t.step("stop the idling server", async () => { + const stop_output = new Deno.Command( + quartoCmd(), + { args: ["call", "engine", "julia", "stop"], cwd: juliaTestDir }, + ).outputSync(); + assertSuccess(stop_output); + assertStderrIncludes(stop_output, "Server stopped"); + + await delay(2000); + + const log_output = new Deno.Command( + quartoCmd(), + { args: ["call", "engine", "julia", "log"], cwd: juliaTestDir }, + ).outputSync(); + assertSuccess(log_output); + assertStdoutIncludes(log_output, "Server stopped"); + }); +}); diff --git a/tests/smoke/julia-engine/render.test.ts b/tests/smoke/julia-engine/render.test.ts new file mode 100644 index 0000000000..91e66751d4 --- /dev/null +++ b/tests/smoke/julia-engine/render.test.ts @@ -0,0 +1,55 @@ +import { assert, assertMatch } from "jsr:@std/assert"; +import { join } from "jsr:@std/path"; +import { existsSync } from "jsr:@std/fs/exists"; + +const isWindows = Deno.build.os === "windows"; +function quartoCmd(): string { + return isWindows ? "quarto.cmd" : "quarto"; +} +function docs(path: string): string { + return join("docs", path); +} + +function renderQmd( + file: string, + args: string[] = [], + cwd?: string, +): Deno.CommandOutput { + const allArgs = ["render", file, ...args]; + const output = new Deno.Command(quartoCmd(), { args: allArgs, cwd }) + .outputSync(); + if (!output.success) { + console.error("Render failed:"); + console.error("stdout:\n" + new TextDecoder().decode(output.stdout)); + console.error("stderr:\n" + new TextDecoder().decode(output.stderr)); + throw new Error(`quarto render ${file} failed`); + } + return output; +} + +Deno.test("source ranges with includes", async () => { + const dir = docs("julia-engine/source-ranges"); + const input = join(dir, "source-ranges-test.qmd"); + renderQmd(input, ["--to", "markdown"]); + + const outputFile = join(dir, "source-ranges-test.md"); + assert(existsSync(outputFile), `Output file ${outputFile} should exist`); + const content = await Deno.readTextFile(outputFile); + + // The julia code outputs __FILE__:__LINE__. Verify source range mapping: + // line 10 of source-ranges-test.qmd and line 2 of _included.qmd + assertMatch(content, /source-ranges-test\.qmd:10/m); + assertMatch(content, /_included\.qmd:2/m); + + try { Deno.removeSync(outputFile); } catch { /* ok */ } +}); + +Deno.test("engine reordering", () => { + const dir = docs("julia-engine/engine-reordering"); + renderQmd("notebook.qmd", ["--to", "html"], dir); + + const outputFile = join(dir, "notebook.html"); + assert(existsSync(outputFile), `Output file ${outputFile} should exist`); + + try { Deno.removeSync(outputFile); } catch { /* ok */ } +}); From 25ad81fb334220fd8c31d77df62332d5ad0f094e Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Thu, 19 Mar 2026 11:40:36 +0100 Subject: [PATCH 3/6] chore: remove Julia resources and tests moved to julia-engine extension These files now live in the julia-engine extension repo at PumasAI/quarto-julia-engine and are bundled into the extension or merged into the test tree at CI time. Removed: - src/resources/julia/ (bundled into _extensions/julia-engine/) - tests/smoke/call/engine/julia/ (moved to extension tests/) - tests/docs/call/engine/julia/ (moved to extension tests/) - tests/docs/smoke-all/julia/ (moved to extension tests/) - tests/docs/smoke-all/engine-reordering/julia-engine/ (moved to extension tests/) --- src/resources/julia/Project.toml | 5 - src/resources/julia/ensure_environment.jl | 32 --- src/resources/julia/quartonotebookrunner.jl | 57 ----- .../start_quartonotebookrunner_detached.jl | 17 -- tests/docs/call/engine/julia/_quarto.yml | 3 - tests/docs/call/engine/julia/sleep.qmd | 9 - .../julia-engine/_quarto.yml | 3 - .../julia-engine/notebook.qmd | 10 - tests/docs/smoke-all/julia/_included.qmd | 3 - .../smoke-all/julia/source-ranges-test.qmd | 16 -- tests/smoke/call/engine/julia/julia.test.ts | 198 ------------------ 11 files changed, 353 deletions(-) delete mode 100644 src/resources/julia/Project.toml delete mode 100644 src/resources/julia/ensure_environment.jl delete mode 100644 src/resources/julia/quartonotebookrunner.jl delete mode 100644 src/resources/julia/start_quartonotebookrunner_detached.jl delete mode 100644 tests/docs/call/engine/julia/_quarto.yml delete mode 100644 tests/docs/call/engine/julia/sleep.qmd delete mode 100644 tests/docs/smoke-all/engine-reordering/julia-engine/_quarto.yml delete mode 100644 tests/docs/smoke-all/engine-reordering/julia-engine/notebook.qmd delete mode 100644 tests/docs/smoke-all/julia/_included.qmd delete mode 100644 tests/docs/smoke-all/julia/source-ranges-test.qmd delete mode 100644 tests/smoke/call/engine/julia/julia.test.ts diff --git a/src/resources/julia/Project.toml b/src/resources/julia/Project.toml deleted file mode 100644 index 94def5bfc2..0000000000 --- a/src/resources/julia/Project.toml +++ /dev/null @@ -1,5 +0,0 @@ -[deps] -QuartoNotebookRunner = "4c0109c6-14e9-4c88-93f0-2b974d3468f4" - -[compat] -QuartoNotebookRunner = "=0.17.4" diff --git a/src/resources/julia/ensure_environment.jl b/src/resources/julia/ensure_environment.jl deleted file mode 100644 index 3274a230f8..0000000000 --- a/src/resources/julia/ensure_environment.jl +++ /dev/null @@ -1,32 +0,0 @@ -using Pkg -using TOML - -# If the manifest was resolved with the exact Project.toml that we have copied -# over from quarto's resource folder, then we just ensure that it is instantiated, -# all packages are downloaded correctly, etc. -# Otherwise, we update to get the newest packages fulfilling the compat bounds set in -# the Project.toml. - -function manifest_has_correct_julia_version() - project_file = Base.active_project() - manifest_file = joinpath(dirname(project_file), "Manifest.toml") - version = VersionNumber(TOML.parsefile(manifest_file)["julia_version"]) - return version.major == VERSION.major && version.minor == VERSION.minor -end - -is_manifest_current = @static if VERSION < v"1.11.0-DEV.1135" - Pkg.is_manifest_current() -else - Pkg.is_manifest_current(dirname(Base.active_project())) -end - -manifest_matches_project_toml = is_manifest_current === true # this returns nothing if there's no manifest - -if manifest_matches_project_toml && manifest_has_correct_julia_version() - Pkg.instantiate() -else - Pkg.update() -end -# not strictly necessary, but in case of precompilation errors this will -# actually print them out explicitly -Pkg.precompile() diff --git a/src/resources/julia/quartonotebookrunner.jl b/src/resources/julia/quartonotebookrunner.jl deleted file mode 100644 index d2a394da98..0000000000 --- a/src/resources/julia/quartonotebookrunner.jl +++ /dev/null @@ -1,57 +0,0 @@ -logfile = ARGS[2] -filehandle = open(logfile, "w") - -# We cannot start Julia in a way such that it uses line buffering or no buffering (see https://github.com/JuliaLang/julia/issues/13050) -# which means that if we just redirect all its output into a logfile (for example with `pipeline(julia_cmd, stdout = logfile)`) -# it will not actually write to the logfile until the buffer is filled or it's explicitly flushed, which can take a while. -# So when we check the logfile, we don't actually see anything if the buffer is not filled first, which -# may never be if there's little output. So instead we redirect stdout and stderr to a pipe which we manually check -# for available data in shorter intervals. We then write the data to the logfile and flush. - -pipe = Pipe() -pipe_with_color = IOContext(pipe, :color => Base.get_have_color()) -redirect_stdout(pipe_with_color) -redirect_stderr(pipe_with_color) - -function update_logfile() - data = readavailable(pipe) - if !isempty(data) - write(filehandle, data) - flush(filehandle) - end - return -end - -@async while true - update_logfile() - sleep(1) -end - -# we might lose printout from crashes if we don't do another update at the end -atexit() do - update_logfile() -end - -using Dates: now -@info "Log started at $(now())" - -using QuartoNotebookRunner -using Sockets - -transport_file = ARGS[1] -transport_dir = dirname(transport_file) - -atexit() do - rm(transport_file; force=true) -end - -server = QuartoNotebookRunner.serve(; timeout = 300) -port = server.port - -open(transport_file, "w") do io - println(io, """{"port": $port, "pid": $(Base.Libc.getpid()), "key": "$(server.key)"}""") -end - -@info "Starting server at $(now())" -wait(server) -@info "Server stopped at $(now())" diff --git a/src/resources/julia/start_quartonotebookrunner_detached.jl b/src/resources/julia/start_quartonotebookrunner_detached.jl deleted file mode 100644 index 3771ca5232..0000000000 --- a/src/resources/julia/start_quartonotebookrunner_detached.jl +++ /dev/null @@ -1,17 +0,0 @@ -# it appears that deno cannot launch detached processes https://github.com/denoland/deno/issues/5501 -# so we use an indirection where we start the detached julia process using julia itself -julia_bin = ARGS[1] -project = ARGS[2] -julia_file = ARGS[3] -transport_file = ARGS[4] -logfile = ARGS[5] - -if length(ARGS) > 5 - error("Too many arguments") -end - -env = copy(ENV) -env["JULIA_LOAD_PATH"] = "@:@stdlib" # ignore the main env -cmd = `$julia_bin --startup-file=no --project=$project $julia_file $transport_file $logfile` -cmd = setenv(cmd, env) -run(detach(cmd), wait = false) diff --git a/tests/docs/call/engine/julia/_quarto.yml b/tests/docs/call/engine/julia/_quarto.yml deleted file mode 100644 index 5d28962fff..0000000000 --- a/tests/docs/call/engine/julia/_quarto.yml +++ /dev/null @@ -1,3 +0,0 @@ -project: - type: default - diff --git a/tests/docs/call/engine/julia/sleep.qmd b/tests/docs/call/engine/julia/sleep.qmd deleted file mode 100644 index 6e8011d326..0000000000 --- a/tests/docs/call/engine/julia/sleep.qmd +++ /dev/null @@ -1,9 +0,0 @@ ---- -engine: julia -params: - sleep_duration: 0 ---- - -```{julia} -sleep(sleep_duration) -``` \ No newline at end of file diff --git a/tests/docs/smoke-all/engine-reordering/julia-engine/_quarto.yml b/tests/docs/smoke-all/engine-reordering/julia-engine/_quarto.yml deleted file mode 100644 index 3aaa4272ac..0000000000 --- a/tests/docs/smoke-all/engine-reordering/julia-engine/_quarto.yml +++ /dev/null @@ -1,3 +0,0 @@ -project: - type: default -engines: ["julia"] diff --git a/tests/docs/smoke-all/engine-reordering/julia-engine/notebook.qmd b/tests/docs/smoke-all/engine-reordering/julia-engine/notebook.qmd deleted file mode 100644 index 3445e8af0a..0000000000 --- a/tests/docs/smoke-all/engine-reordering/julia-engine/notebook.qmd +++ /dev/null @@ -1,10 +0,0 @@ -```{julia} -using Test -@test haskey( - Base.loaded_modules, - Base.PkgId( - Base.UUID("38328d9c-a911-4051-bc06-3f7f556ffeda"), - "QuartoNotebookWorker", - ) -) -``` \ No newline at end of file diff --git a/tests/docs/smoke-all/julia/_included.qmd b/tests/docs/smoke-all/julia/_included.qmd deleted file mode 100644 index 1be592fa71..0000000000 --- a/tests/docs/smoke-all/julia/_included.qmd +++ /dev/null @@ -1,3 +0,0 @@ -```{julia} -"$(@__FILE__):$(@__LINE__)" -``` \ No newline at end of file diff --git a/tests/docs/smoke-all/julia/source-ranges-test.qmd b/tests/docs/smoke-all/julia/source-ranges-test.qmd deleted file mode 100644 index a1947ac784..0000000000 --- a/tests/docs/smoke-all/julia/source-ranges-test.qmd +++ /dev/null @@ -1,16 +0,0 @@ ---- -title: "Test Julia source ranges with includes" -format: markdown -engine: julia -_quarto: - tests: - markdown: - ensureFileRegexMatches: - - ['source-ranges-test\.qmd:15', '_included\.qmd:2'] ---- - -{{< include _included.qmd >}} - -```{julia} -"$(@__FILE__):$(@__LINE__)" -``` diff --git a/tests/smoke/call/engine/julia/julia.test.ts b/tests/smoke/call/engine/julia/julia.test.ts deleted file mode 100644 index 351a2061e7..0000000000 --- a/tests/smoke/call/engine/julia/julia.test.ts +++ /dev/null @@ -1,198 +0,0 @@ -import { assert, assertStringIncludes } from "testing/asserts"; -import { docs, quartoDevCmd } from "../../../../utils.ts"; -import { existsSync } from "fs/exists"; -import { sleep } from "../../../../../src/core/wait.ts"; -import { quartoRuntimeDir } from "../../../../../src/core/appdirs.ts"; -import { join } from "../../../../../src/deno_ral/path.ts"; - -const juliaTestDir = docs("call/engine/julia"); -const sleepQmd = "sleep.qmd"; -assert(existsSync(docs("call/engine/julia/sleep.qmd"))); - -function assertSuccess(output: Deno.CommandOutput) { - if (!output.success) { - console.error("Command failed:"); - console.error("stdout:\n" + new TextDecoder().decode(output.stdout)); - console.error("stderr:\n" + new TextDecoder().decode(output.stderr)); - throw new Error("Command execution was not successful"); - } -} - -function assertStdoutIncludes(output: Deno.CommandOutput, str: string) { - assertStringIncludes(new TextDecoder().decode(output.stdout), str); -} -function assertStderrIncludes(output: Deno.CommandOutput, str: string) { - assertStringIncludes(new TextDecoder().decode(output.stderr), str); -} - -Deno.test("julia engine", async (t) => { - // Setup: Clean up any leftover server state before running tests - const killcmd = new Deno.Command( - quartoDevCmd(), - { args: ["call", "engine", "julia", "kill"], cwd: juliaTestDir }, - ).outputSync(); - assertSuccess(killcmd); - - const transportFile = join(quartoRuntimeDir("julia"), "julia_transport.txt"); - const logFile = join(quartoRuntimeDir("julia"), "julia_server_log.txt"); - - try { - await Deno.remove(transportFile); - } catch { - // File might not exist, that's okay - } - - try { - await Deno.remove(logFile); - } catch { - // File might not exist, that's okay - } - - // Now run all the actual tests as steps - await t.step("kill without server running", () => { - const output = new Deno.Command( - quartoDevCmd(), - { args: ["call", "engine", "julia", "kill"], cwd: juliaTestDir }, - ).outputSync(); - assertSuccess(output); - assertStderrIncludes(output, "Julia control server is not running."); - }); - - await t.step("status without server running", () => { - const output = new Deno.Command( - quartoDevCmd(), - { args: ["call", "engine", "julia", "status"], cwd: juliaTestDir }, - ).outputSync(); - assertSuccess(output); - assertStderrIncludes(output, "Julia control server is not running."); - }); - - await t.step("log file doesn't exist", () => { - const log_output = new Deno.Command( - quartoDevCmd(), - { args: ["call", "engine", "julia", "log"], cwd: juliaTestDir }, - ).outputSync(); - assertSuccess(log_output); - assertStderrIncludes(log_output, "Server log file doesn't exist"); - }); - - await t.step("status with server and worker running", () => { - const render_output = new Deno.Command( - quartoDevCmd(), - { - args: [ - "render", - sleepQmd, - "-P", - "sleep_duration:0", - "--execute-daemon", - "60", - ], - cwd: juliaTestDir, - }, - ).outputSync(); - assertSuccess(render_output); - - const status_output = new Deno.Command( - quartoDevCmd(), - { args: ["call", "engine", "julia", "status"], cwd: juliaTestDir }, - ).outputSync(); - assertSuccess(status_output); - assertStdoutIncludes(status_output, "workers active: 1"); - }); - - await t.step("closing an idling worker", () => { - const close_output = new Deno.Command( - quartoDevCmd(), - { - args: ["call", "engine", "julia", "close", sleepQmd], - cwd: juliaTestDir, - }, - ).outputSync(); - assertSuccess(close_output); - assertStderrIncludes(close_output, "Worker closed successfully"); - - const status_output = new Deno.Command( - quartoDevCmd(), - { args: ["call", "engine", "julia", "status"], cwd: juliaTestDir }, - ).outputSync(); - assertSuccess(status_output); - assertStdoutIncludes(status_output, "workers active: 0"); - }); - - await t.step("force-closing a running worker", async () => { - // spawn a long-running command - const render_cmd = new Deno.Command( - quartoDevCmd(), - { - args: ["render", sleepQmd, "-P", "sleep_duration:30"], - cwd: juliaTestDir, - }, - ).output(); - - await sleep(3000); - - const close_output = new Deno.Command( - quartoDevCmd(), - { - args: ["call", "engine", "julia", "close", sleepQmd], - cwd: juliaTestDir, - }, - ).outputSync(); - assertStderrIncludes(close_output, "worker is busy"); - - const status_output = new Deno.Command( - quartoDevCmd(), - { args: ["call", "engine", "julia", "status"], cwd: juliaTestDir }, - ).outputSync(); - assertSuccess(status_output); - assertStdoutIncludes(status_output, "workers active: 1"); - - const force_close_output = new Deno.Command( - quartoDevCmd(), - { - args: ["call", "engine", "julia", "close", "--force", sleepQmd], - cwd: juliaTestDir, - }, - ).outputSync(); - assertSuccess(force_close_output); - assertStderrIncludes(force_close_output, "Worker force-closed successfully"); - - const status_output_2 = new Deno.Command( - quartoDevCmd(), - { args: ["call", "engine", "julia", "status"], cwd: juliaTestDir }, - ).outputSync(); - assertSuccess(status_output_2); - assertStdoutIncludes(status_output_2, "workers active: 0"); - - const render_output = await render_cmd; - assertStderrIncludes(render_output, "File was force-closed during run"); - }); - - await t.step("log exists", () => { - const log_output = new Deno.Command( - quartoDevCmd(), - { args: ["call", "engine", "julia", "log"], cwd: juliaTestDir }, - ).outputSync(); - assertSuccess(log_output); - assertStdoutIncludes(log_output, "Log started at"); - }); - - await t.step("stop the idling server", async () => { - const stop_output = new Deno.Command( - quartoDevCmd(), - { args: ["call", "engine", "julia", "stop"], cwd: juliaTestDir }, - ).outputSync(); - assertSuccess(stop_output); - assertStderrIncludes(stop_output, "Server stopped"); - - await sleep(2000); // allow a little bit of time for the server to stop and the log message to be written - - const log_output = new Deno.Command( - quartoDevCmd(), - { args: ["call", "engine", "julia", "log"], cwd: juliaTestDir }, - ).outputSync(); - assertSuccess(log_output); - assertStdoutIncludes(log_output, "Server stopped"); - }); -}); From b84d58b3778f8396b36384950cd478e2d9863bba Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Thu, 19 Mar 2026 11:41:34 +0100 Subject: [PATCH 4/6] ci: merge julia-engine extension tests into test tree before running MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Copy docs and smoke test files from the julia-engine subtree into the main test directories so they are discovered by the test runner. This avoids duplicating tests — the extension repo is the single source of truth. --- .github/workflows/test-smokes.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/test-smokes.yml b/.github/workflows/test-smokes.yml index 863e0d951b..49e55a00f6 100644 --- a/.github/workflows/test-smokes.yml +++ b/.github/workflows/test-smokes.yml @@ -241,6 +241,17 @@ jobs: echo "Julia Jupyter:" uv run julia --project=. -e "import IJulia;println(IJulia.JUPYTER);println(IJulia.find_jupyter_subcommand(\"notebook\"))" + # Julia engine tests live in the julia-engine extension repo + # (PumasAI/quarto-julia-engine) and are pulled in via git subtree. + # We merge them into the test tree here so the test runner discovers + # them alongside the other smoke tests, avoiding test duplication. + - name: Merge julia-engine tests into test tree + shell: bash + run: | + SUBTREE=src/resources/extension-subtrees/julia-engine/tests + cp -r "$SUBTREE/docs/julia-engine" tests/docs/julia-engine + cp -r "$SUBTREE/smoke/julia-engine" tests/smoke/julia-engine + - name: Setup timing file for timed test if: ${{ matrix.time-test == true }} run: | From 6957302e46de344783e6da37d3350cb19325a7ef Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Thu, 19 Mar 2026 11:50:12 +0100 Subject: [PATCH 5/6] fix: update julia-engine subtree remote URL to PumasAI/quarto-julia-engine --- src/command/dev-call/pull-git-subtree/cmd.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/command/dev-call/pull-git-subtree/cmd.ts b/src/command/dev-call/pull-git-subtree/cmd.ts index d71e67bc2b..8570902bdf 100644 --- a/src/command/dev-call/pull-git-subtree/cmd.ts +++ b/src/command/dev-call/pull-git-subtree/cmd.ts @@ -21,7 +21,7 @@ const SUBTREES: SubtreeConfig[] = [ { name: "julia-engine", prefix: "src/resources/extension-subtrees/julia-engine", - remoteUrl: "https://github.com/gordonwoodhull/quarto-julia-engine.git", + remoteUrl: "https://github.com/PumasAI/quarto-julia-engine.git", remoteBranch: "main", }, { From 1313ad4f75f857ff6629bd3f7b5504cc7aadc862 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Thu, 19 Mar 2026 12:10:20 +0100 Subject: [PATCH 6/6] ci: extract extension test merge into reusable composite action Used in both test-smokes-parallel.yml (for bucket discovery) and test-smokes.yml (for each test runner job's fresh checkout). --- .github/actions/merge-extension-tests/action.yml | 16 ++++++++++++++++ .github/workflows/test-smokes-parallel.yml | 2 ++ .github/workflows/test-smokes.yml | 11 +---------- 3 files changed, 19 insertions(+), 10 deletions(-) create mode 100644 .github/actions/merge-extension-tests/action.yml diff --git a/.github/actions/merge-extension-tests/action.yml b/.github/actions/merge-extension-tests/action.yml new file mode 100644 index 0000000000..2091b9c95f --- /dev/null +++ b/.github/actions/merge-extension-tests/action.yml @@ -0,0 +1,16 @@ +name: "Merge extension tests into test tree" +description: > + Copies test files from extension subtrees into the main test directories + so they are discovered by the test runner and bucket creation. + Extension tests live in their own repos (single source of truth) and are + pulled in via git subtree — this step avoids duplicating them. + +runs: + using: "composite" + steps: + - name: Merge julia-engine tests + shell: bash + run: | + SUBTREE=src/resources/extension-subtrees/julia-engine/tests + cp -r "$SUBTREE/docs/julia-engine" tests/docs/julia-engine + cp -r "$SUBTREE/smoke/julia-engine" tests/smoke/julia-engine diff --git a/.github/workflows/test-smokes-parallel.yml b/.github/workflows/test-smokes-parallel.yml index d36cbe86b6..48e0240113 100644 --- a/.github/workflows/test-smokes-parallel.yml +++ b/.github/workflows/test-smokes-parallel.yml @@ -60,6 +60,8 @@ jobs: with: version: ${{ github.ref == 'refs/heads/main' && 'pre-release' || 'release' }} + - uses: ./.github/actions/merge-extension-tests + - name: Create Job for tests id: tests-buckets run: | diff --git a/.github/workflows/test-smokes.yml b/.github/workflows/test-smokes.yml index 49e55a00f6..bd70ac6964 100644 --- a/.github/workflows/test-smokes.yml +++ b/.github/workflows/test-smokes.yml @@ -241,16 +241,7 @@ jobs: echo "Julia Jupyter:" uv run julia --project=. -e "import IJulia;println(IJulia.JUPYTER);println(IJulia.find_jupyter_subcommand(\"notebook\"))" - # Julia engine tests live in the julia-engine extension repo - # (PumasAI/quarto-julia-engine) and are pulled in via git subtree. - # We merge them into the test tree here so the test runner discovers - # them alongside the other smoke tests, avoiding test duplication. - - name: Merge julia-engine tests into test tree - shell: bash - run: | - SUBTREE=src/resources/extension-subtrees/julia-engine/tests - cp -r "$SUBTREE/docs/julia-engine" tests/docs/julia-engine - cp -r "$SUBTREE/smoke/julia-engine" tests/smoke/julia-engine + - uses: ./.github/actions/merge-extension-tests - name: Setup timing file for timed test if: ${{ matrix.time-test == true }}