diff --git a/src/JuliaWorkspaces.jl b/src/JuliaWorkspaces.jl index b3fce9e..d1b4f1b 100644 --- a/src/JuliaWorkspaces.jl +++ b/src/JuliaWorkspaces.jl @@ -7,6 +7,8 @@ using Salsa using AutoHashEquals +include("utils.jl") + include("compat.jl") import Pkg diff --git a/src/fileio.jl b/src/fileio.jl index 2b9e950..b7dd4db 100644 --- a/src/fileio.jl +++ b/src/fileio.jl @@ -11,7 +11,8 @@ end function is_path_manifest_file(path) basename_lower_case = basename(lowercase(path)) - return basename_lower_case=="manifest.toml" || basename_lower_case=="juliamanifest.toml" + # Manifest.toml, Manifest-v1.11.toml, JuliaManifest.toml, etc. + return occursin(r"^(julia)?manifest(\-v\d+(\.\d+)*)?\.toml$", basename_lower_case) end function is_path_lintconfig_file(path) diff --git a/src/layer_testitems.jl b/src/layer_testitems.jl index 33585cc..4a9e6dc 100644 --- a/src/layer_testitems.jl +++ b/src/layer_testitems.jl @@ -109,32 +109,50 @@ Salsa.@derived function derived_testenv(rt, uri) projects = derived_project_folders(rt) packages = derived_package_folders(rt) - project_uri = find_project_for_file(projects, uri) + project_uri_guess = @something( + find_project_for_file(projects, uri), + input_fallback_test_project(rt), + Some(nothing) + ) package_uri = find_package_for_file(packages, uri) - if project_uri === nothing - project_uri = input_fallback_test_project(rt) - end - - package_name = package_uri === nothing ? nothing : derived_package(rt, package_uri).name + package_name = + if isnothing(package_uri) + nothing + else + safe_getproperty(derived_package(rt, package_uri), :name) + end - if project_uri == package_uri - elseif project_uri in projects - relevant_project = derived_project(rt, project_uri) + project_uri = + if project_uri_guess == package_uri + project_uri_guess + elseif project_uri_guess in projects + relevant_project = derived_project(rt, project_uri_guess) + + if isnothing(relevant_project) + nothing + elseif any(i->i.uri == package_uri, collect(values(relevant_project.deved_packages))) + project_uri_guess + else + nothing + end + else + nothing + end - if findfirst(i->i.uri == package_uri, collect(values(relevant_project.deved_packages))) === nothing - project_uri = nothing + project_env_content_hash = + if isnothing(project_uri) + hash(nothing) + else + safe_getproperty(derived_project(rt, project_uri), :content_hash) end - else - project_uri = nothing - end - env_content_hash = isnothing(project_uri) ? hash(nothing) : derived_project(rt, project_uri).content_hash - if package_uri===nothing - env_content_hash = hash(nothing, env_content_hash) - else - env_content_hash = hash(derived_package(rt, package_uri).content_hash) - end + env_content_hash = + if isnothing(package_uri) + hash(project_env_content_hash) + else + hash(safe_getproperty(derived_package(rt, package_uri), :content_hash)) + end # We construct a string for the env content hash here so that later when we # deserialize it with JSON.jl we don't end up with Int conversion issues diff --git a/src/utils.jl b/src/utils.jl new file mode 100644 index 0000000..b066725 --- /dev/null +++ b/src/utils.jl @@ -0,0 +1,7 @@ +@inline function safe_getproperty(x, s::Symbol) + if isnothing(x) + return nothing + else + return getproperty(x, s) + end +end diff --git a/test/test_testitems.jl b/test/test_testitems.jl index a1ac98f..21aaed5 100644 --- a/test/test_testitems.jl +++ b/test/test_testitems.jl @@ -439,3 +439,118 @@ end @test ti.name == "Test1" end + +@testitem "versioned manifest files are detected" begin + using JuliaWorkspaces + using JuliaWorkspaces.URIs2: filepath2uri + + mktempdir() do temp_dir + # Create project with versioned manifest + project_dir = joinpath(temp_dir, "VersionedProject") + mkpath(project_dir) + + project_file = joinpath(project_dir, "Project.toml") + write(project_file, """ +name = "VersionedProject" +uuid = "12345678-1234-1234-1234-123456789abc" +version = "0.1.0" + +[deps] +Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +""") + + # Create versioned manifest + versioned_manifest = joinpath(project_dir, "Manifest-v$(VERSION.major).$(VERSION.minor).toml") + write(versioned_manifest, """ +julia_version = "$(VERSION.major).$(VERSION.minor).$(VERSION.patch)" +manifest_format = "2.0" +project_hash = "test" + +[[deps.Random]] +deps = ["SHA", "Serialization"] +uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +version = "1.10.0" + +[[deps.SHA]] +uuid = "ea8e919c-285b-4e28-92e2-21d1dda8b7a7" +version = "0.7.0" + +[[deps.Serialization]] +uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" +version = "1.10.0" +""") + + # Add to workspace + project_uri = filepath2uri(project_file) + manifest_uri = filepath2uri(versioned_manifest) + folder_uri = filepath2uri(project_dir) + + jw = JuliaWorkspace() + add_file!(jw, TextFile(project_uri, SourceText(read(project_file, String), "toml"))) + add_file!(jw, TextFile(manifest_uri, SourceText(read(versioned_manifest, String), "toml"))) + + # Test that versioned manifest IS now detected + rt = jw.runtime + potential_projects = JuliaWorkspaces.derived_potential_project_folders(rt) + + @test haskey(potential_projects, folder_uri) + project_info = potential_projects[folder_uri] + @test project_info.project_file !== nothing + @test project_info.manifest_file !== nothing # FIXED: versioned manifest now detected + + # This should now return a valid project + derived_result = JuliaWorkspaces.derived_project(rt, folder_uri) + @test derived_result !== nothing + @test derived_result isa JuliaWorkspaces.JuliaProject + end +end + +@testitem "handle missing manifest gracefully" begin + using JuliaWorkspaces + using JuliaWorkspaces.URIs2: filepath2uri + + mktempdir() do temp_dir + # Create a simple project that will work + project_dir = joinpath(temp_dir, "SimpleProject") + mkpath(project_dir) + + project_file = joinpath(project_dir, "Project.toml") + write( + project_file, + """ +name = "SimpleProject" +uuid = "12345678-1234-1234-1234-123456789abc" +version = "0.1.0" +""", + ) + + # NO MANIFEST - this makes derived_project return nothing + + # Create Julia file + test_file = joinpath(temp_dir, "test.jl") + write(test_file, "# Test file") + + # Add to workspace + project_uri = filepath2uri(project_file) + test_uri = filepath2uri(test_file) + folder_uri = filepath2uri(project_dir) + + jw = JuliaWorkspace() + add_file!(jw, TextFile(project_uri, SourceText(read(project_file, String), "toml"))) + add_file!(jw, TextFile(test_uri, SourceText("# test", "julia"))) + + # Set project as fallback test project + JuliaWorkspaces.set_input_fallback_test_project!(jw.runtime, folder_uri) + + rt = jw.runtime + + # Verify derived_project returns nothing (no manifest) + derived_result = JuliaWorkspaces.derived_project(rt, folder_uri) + @test derived_result === nothing + + # This should work with defensive programming - no crash on .content_hash + test_env = get_test_env(jw, test_uri) + @test test_env isa JuliaWorkspaces.JuliaTestEnv + @test test_env.project_uri === nothing # Should be set to nothing due to lack of manifest + end +end