Skip to content

Commit 3adcf4a

Browse files
authored
ensure we do not end up in a situation where both tree hash and path is set (#4467)
1 parent 904a769 commit 3adcf4a

File tree

5 files changed

+109
-14
lines changed

5 files changed

+109
-14
lines changed

src/Operations.jl

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -252,9 +252,14 @@ function load_all_deps(
252252
for pkg in pkgs
253253
path, repo = get_path_repo(env.project, env.project_file, env.manifest_file, pkg.name)
254254
if path !== nothing
255+
# Path from [sources] takes precedence - clear tree_hash and repo from manifest
256+
pkg.tree_hash = nothing
257+
pkg.repo = GitRepo() # Clear any repo info
255258
pkg.path = path
256259
end
257260
if repo.source !== nothing
261+
# Repo from [sources] takes precedence - clear path from manifest
262+
pkg.path = nothing
258263
pkg.repo.source = repo.source
259264
end
260265
if repo.rev !== nothing
@@ -2224,7 +2229,7 @@ function up_load_versions!(ctx::Context, pkg::PackageSpec, entry::PackageEntry,
22242229
if pkg.path === nothing
22252230
pkg.tree_hash = entry.tree_hash
22262231
end
2227-
elseif entry.repo.source !== nothing || source_repo.source !== nothing # repo packages have a version but are treated specially
2232+
elseif source_path === nothing && pkg.path === nothing && (entry.repo.source !== nothing || source_repo.source !== nothing) # repo packages have a version but are treated specially
22282233
if source_repo.source !== nothing
22292234
pkg.repo = source_repo
22302235
else
@@ -2257,10 +2262,12 @@ end
22572262
up_load_manifest_info!(pkg::PackageSpec, ::Nothing) = nothing
22582263
function up_load_manifest_info!(pkg::PackageSpec, entry::PackageEntry)
22592264
pkg.name = entry.name # TODO check name is same
2260-
if pkg.repo == GitRepo()
2265+
# Only restore repo from manifest if we don't already have a path set
2266+
if pkg.repo == GitRepo() && pkg.path === nothing
22612267
pkg.repo = entry.repo # TODO check that repo is same
22622268
end
2263-
if pkg.path === nothing
2269+
# Only set path if tree_hash is not already set (to avoid invalid state where both are set)
2270+
if pkg.path === nothing && pkg.repo == GitRepo() && pkg.tree_hash === nothing
22642271
pkg.path = entry.path
22652272
end
22662273
return pkg.pinned = entry.pinned

src/Types.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,6 +1040,7 @@ function handle_repo_add!(ctx::Context, pkg::PackageSpec)
10401040
pkgerror("Did not find subdirectory `$(pkg.repo.subdir)`")
10411041
end
10421042
end
1043+
@assert pkg.path === nothing
10431044
pkg.tree_hash = SHA1(string(LibGit2.GitHash(tree_hash_object)))
10441045

10451046
# If we already resolved a uuid, we can bail early if this package is already installed at the current tree_hash

test/sources.jl

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module SourcesTest
33
import ..Pkg # ensure we are using the correct Pkg
44
using Test, Pkg
55
using ..Utils
6+
using UUIDs
67

78
temp_pkg_dir() do project_path
89
@testset "test Project.toml [sources]" begin
@@ -152,6 +153,91 @@ temp_pkg_dir() do project_path
152153
end
153154
end
154155
end
156+
157+
# Regression test for https://github.com/JuliaLang/Pkg.jl/issues/4337
158+
# Switching between path and repo sources should not cause assertion error
159+
@testset "switching between path and repo sources (#4337)" begin
160+
mktempdir() do tmp
161+
cd(tmp) do
162+
# Create a local package and initialize it as a git repo
163+
local_pkg_uuid = UUID("00000000-0000-0000-0000-000000000001")
164+
mkdir("LocalPkg")
165+
write(
166+
joinpath("LocalPkg", "Project.toml"), """
167+
name = "LocalPkg"
168+
uuid = "$local_pkg_uuid"
169+
version = "0.1.0"
170+
"""
171+
)
172+
mkdir(joinpath("LocalPkg", "src"))
173+
write(joinpath("LocalPkg", "src", "LocalPkg.jl"), "module LocalPkg end")
174+
175+
# Initialize as a git repo
176+
git_init_and_commit("LocalPkg")
177+
178+
# Get the absolute path for file:// URL
179+
local_pkg_url = make_file_url(abspath("LocalPkg"))
180+
181+
# Create test project with path source
182+
write(
183+
"Project.toml", """
184+
[deps]
185+
LocalPkg = "$local_pkg_uuid"
186+
187+
[sources]
188+
LocalPkg = { path = "LocalPkg" }
189+
"""
190+
)
191+
192+
with_current_env() do
193+
# Initial resolve with path source
194+
Pkg.resolve()
195+
manifest = Pkg.Types.read_manifest("Manifest.toml")
196+
@test manifest[local_pkg_uuid].path !== nothing
197+
@test manifest[local_pkg_uuid].tree_hash === nothing
198+
@test manifest[local_pkg_uuid].repo.source === nothing
199+
# Update should work without error
200+
Pkg.update()
201+
202+
# Switch to repo source using file:// protocol
203+
write(
204+
"Project.toml", """
205+
[deps]
206+
LocalPkg = "$local_pkg_uuid"
207+
208+
[sources]
209+
LocalPkg = { url = "$local_pkg_url", rev = "HEAD" }
210+
"""
211+
)
212+
213+
# This should NOT cause an assertion error about tree_hash and path both being set
214+
Pkg.update()
215+
manifest = Pkg.Types.read_manifest("Manifest.toml")
216+
@test manifest[local_pkg_uuid].path === nothing
217+
@test manifest[local_pkg_uuid].tree_hash !== nothing
218+
@test manifest[local_pkg_uuid].repo.source !== nothing
219+
220+
# Switch back to path source
221+
write(
222+
"Project.toml", """
223+
[deps]
224+
LocalPkg = "$local_pkg_uuid"
225+
226+
[sources]
227+
LocalPkg = { path = "LocalPkg" }
228+
"""
229+
)
230+
231+
# This should work and restore the path source without assertion error
232+
Pkg.update()
233+
manifest = Pkg.Types.read_manifest("Manifest.toml")
234+
@test manifest[local_pkg_uuid].path !== nothing
235+
@test manifest[local_pkg_uuid].tree_hash === nothing
236+
@test manifest[local_pkg_uuid].repo.source === nothing
237+
end
238+
end
239+
end
240+
end
155241
end
156242

157243
end # module

test/subdir.jl

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -65,16 +65,6 @@ function setup_packages_repository(dir)
6565
return package_tree_hash, dep_tree_hash
6666
end
6767

68-
# Convert a path into a file URL.
69-
function make_file_url(path)
70-
# Turn the slashes on Windows. In case the path starts with a
71-
# drive letter, an extra slash will be needed in the file URL.
72-
path = replace(path, "\\" => "/")
73-
if !startswith(path, "/")
74-
path = "/" * path
75-
end
76-
return "file://$(path)"
77-
end
7868

7969
# Create a registry with the two packages `Package` and `Dep`.
8070
function setup_registry(dir, packages_dir_url, package_tree_hash, dep_tree_hash)

test/utils.jl

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ using UUIDs
1111
export temp_pkg_dir, cd_tempdir, isinstalled, write_build, with_current_env,
1212
with_temp_env, with_pkg_env, git_init_and_commit, copy_test_package,
1313
git_init_package, add_this_pkg, TEST_SIG, TEST_PKG, isolate, LOADED_DEPOT,
14-
list_tarball_files, recursive_rm_cov_files, copy_this_pkg_cache
14+
list_tarball_files, recursive_rm_cov_files, copy_this_pkg_cache, make_file_url
1515

1616
const CACHE_DIRECTORY = realpath(mktempdir(; cleanup = true))
1717

@@ -367,4 +367,15 @@ function recursive_rm_cov_files(rootdir::String)
367367
return
368368
end
369369

370+
# Convert a path into a file URL.
371+
function make_file_url(path)
372+
# Turn the slashes on Windows. In case the path starts with a
373+
# drive letter, an extra slash will be needed in the file URL.
374+
path = replace(path, "\\" => "/")
375+
if !startswith(path, "/")
376+
path = "/" * path
377+
end
378+
return "file://$(path)"
379+
end
380+
370381
end

0 commit comments

Comments
 (0)