Skip to content

Commit

Permalink
force_latest_compatible_version: explicitly ask the resolver to loo…
Browse files Browse the repository at this point in the history
…k for a solution
  • Loading branch information
DilumAluthge committed Mar 30, 2021
1 parent a4ade8c commit dcf38d4
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 117 deletions.
151 changes: 80 additions & 71 deletions src/Operations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1421,6 +1421,14 @@ function sandbox(fn::Function, ctx::Context, target::PackageSpec, target_path::S
temp_ctx = Context()
temp_ctx.env.project.deps[target.name] = target.uuid

if force_latest_compatible_version
set_force_latest_compatible_version!(
temp_ctx;
allow_earlier_backwards_compatible_versions,
target_name = target.name,
)
end

try
Pkg.resolve(temp_ctx; io=devnull)
@debug "Using _parent_ dep graph"
Expand All @@ -1441,17 +1449,6 @@ function sandbox(fn::Function, ctx::Context, target::PackageSpec, target_path::S
end
write_env(temp_ctx.env, update_undo = false)

if force_latest_compatible_version
result = check_force_latest_compatible_version(
temp_ctx;
target_name = target.name,
allow_earlier_backwards_compatible_versions,
)
if !result
pkgerror("One or more direct dependencies is not at the latest compatible version")
end
end

# Run sandboxed code
path_sep = Sys.iswindows() ? ';' : ':'
withenv(fn, "JULIA_LOAD_PATH" => "@$(path_sep)$(tmp)", "JULIA_PROJECT" => nothing)
Expand Down Expand Up @@ -1797,77 +1794,89 @@ function status(env::EnvCache, pkgs::Vector{PackageSpec}=PackageSpec[];
end
end

function check_force_latest_compatible_version(ctx::Types.Context;
target_name = nothing,
allow_earlier_backwards_compatible_versions::Bool = true)
direct_deps = load_direct_deps(ctx.env)
direct_deps_uuids = [dep.uuid for dep in direct_deps]
uuid_list = filter(!is_stdlib, direct_deps_uuids)
isempty(uuid_list) && return true
results = check_force_latest_compatible_version.(
Ref(ctx),
uuid_list;
target_name,
allow_earlier_backwards_compatible_versions,
)
return all(results)
end

function check_force_latest_compatible_version(ctx::Types.Context,
uuid::Base.UUID;
target_name= nothing,
allow_earlier_backwards_compatible_versions::Bool = true)
dep = ctx.env.manifest[uuid]
name = dep.name
active_version = dep.version
has_compat = haskey(ctx.env.project.compat, name)
if !has_compat
if name != target_name
@warn(
"Package does not have a [compat] entry",
name, uuid, active_version, target_name,
function set_force_latest_compatible_version!(ctx::Types.Context;
allow_earlier_backwards_compatible_versions::Bool = true,
target_name = nothing)
for dep in load_direct_deps(ctx.env)
if !is_stdlib(dep.uuid)
set_force_latest_compatible_version!(
ctx,
dep.name,
dep.uuid;
allow_earlier_backwards_compatible_versions,
target_name,
)
end
return true
end
compat_entry_string = ctx.env.project.compat[name]
latest_compatible_version = get_latest_compatible_version(
ctx,
uuid,
compat_entry_string,
)
earliest_backwards_compatible_version = get_earliest_backwards_compatible_version(latest_compatible_version)
if allow_earlier_backwards_compatible_versions
result = active_version >= earliest_backwards_compatible_version
else
result = active_version >= latest_compatible_version
end
if !result
@error(
"Package is not at the latest compatible version",
name,
uuid,
compat_entry_string,
active_version,
latest_compatible_version,
end
return nothing
end

function set_force_latest_compatible_version!(ctx::Types.Context,
dep_name::String,
dep_uuid::Base.UUID;
allow_earlier_backwards_compatible_versions::Bool = true,
target_name = nothing)
has_compat = haskey(ctx.env.project.compat, dep_name)
if has_compat
old_compat_entry_string = ctx.env.project.compat[dep_name]
old_compat_entry_spec = Types.semver_spec(old_compat_entry_string)
all_registered_versions = get_all_registered_versions(ctx, dep_uuid)
latest_compatible_version = get_latest_compatible_version(
ctx,
dep_uuid,
old_compat_entry_spec,
all_registered_versions,
)
earliest_backwards_compatible_version = get_earliest_backwards_compatible_version(latest_compatible_version)
all_compatible_earliest_to_latest = get_all_compatible_earliest_to_latest(
old_compat_entry_spec,
earliest_backwards_compatible_version,
allow_earlier_backwards_compatible_versions,
latest_compatible_version,
all_registered_versions,
)
if allow_earlier_backwards_compatible_versions
new_compat_entry_string = join(string.(Ref("="), all_compatible_earliest_to_latest), ", ")
else
new_compat_entry_string = "=$(latest_compatible_version)"
end
ctx.env.project.compat[dep_name] = new_compat_entry_string
@info("Setting new `[compat]` entry: `$(dep_name) = \"$(new_compat_entry_string)\"`")
else
if dep_name != target_name
@warn(
"Package does not have a [compat] entry",
dep_name, dep_uuid, target_name,
)
end
end
return nothing
end

function get_all_compatible_earliest_to_latest(compat_spec::VersionSpec,
earliest_ver::VersionNumber,
latest_ver::VersionNumber,
all_registered_versions::Set{VersionNumber})
compatible_versions = Set{VersionNumber}()
for ver in all_registered_versions
if earliest_ver <= ver <= latest_ver
if ver in compat_spec
push!(compatible_versions, ver)
end
end
end
return result
return sort(collect(compatible_versions))
end

function get_earliest_backwards_compatible_version(ver::Base.VersionNumber)
(ver.major > 0) && return Base.VersionNumber(ver.major, 0, 0)
(ver.minor > 0) && return Base.VersionNumber(0, ver.minor, 0)
return Base.VersionNumber(0, 0, ver.patch)
function get_earliest_backwards_compatible_version(ver::VersionNumber)
(ver.major > 0) && return VersionNumber(ver.major, 0, 0)
(ver.minor > 0) && return VersionNumber(0, ver.minor, 0)
return VersionNumber(0, 0, ver.patch)
end

function get_latest_compatible_version(ctx::Types.Context,
uuid::Base.UUID,
compat_entry_string::AbstractString)
all_registered_versions = get_all_registered_versions(ctx, uuid)
compat_spec = Pkg.Types.semver_spec(compat_entry_string)
compat_spec::VersionSpec,
all_registered_versions::Set{VersionNumber})
compatible_versions = filter(in(compat_spec), all_registered_versions)
latest_compatible_version = maximum(compatible_versions)
return latest_compatible_version
Expand Down
111 changes: 65 additions & 46 deletions test/force_latest_compatible_version.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@ import ..Pkg # ensure we are using the correct Pkg
import ..Utils
using Test

const expected_message_1 = "One or more direct dependencies is not at the latest compatible version"
const expected_message_2 = "Package is not at the latest compatible version"
const expected_message_3 = "Package does not have a [compat] entry"
const expected_message_1 = "Unsatisfiable requirements detected"
const expected_message_2 = "Package does not have a [compat] entry"

const test_package_parent_dir = joinpath(
@__DIR__,
Expand Down Expand Up @@ -84,25 +83,24 @@ const test_package_parent_dir = joinpath(
) == nothing
)
@test_throws(
Pkg.Types.PkgError(expected_message_1),
Pkg.Resolve.ResolverError,
Pkg.test(;
force_latest_compatible_version = true,
allow_earlier_backwards_compatible_versions = false,
),
)
@test_logs(
(:error, expected_message_2),
match_mode=:any,
begin
try
Pkg.test(;
force_latest_compatible_version = true,
allow_earlier_backwards_compatible_versions = false,
)
catch
end
end,
)
let
ex = try
Pkg.test(;
force_latest_compatible_version = true,
allow_earlier_backwards_compatible_versions = false,
)
catch ex
ex
end
@test ex isa Pkg.Resolve.ResolverError
@test occursin(lowercase(strip(expected_message_1)), lowercase(strip(ex.msg)))
end
end

@testset "`allow_earlier_backwards_compatible_versions` = true" begin
Expand Down Expand Up @@ -135,23 +133,22 @@ const test_package_parent_dir = joinpath(
) == nothing
)
@test_throws(
Pkg.Types.PkgError(expected_message_1),
Pkg.Resolve.ResolverError,
Pkg.test(;
force_latest_compatible_version = true,
),
)
@test_logs(
(:error, expected_message_2),
match_mode=:any,
begin
try
Pkg.test(;
force_latest_compatible_version = true,
)
catch
end
end,
)
let
ex = try
Pkg.test(;
force_latest_compatible_version = true,
)
catch ex
ex
end
@test ex isa Pkg.Resolve.ResolverError
@test occursin(lowercase(strip(expected_message_1)), lowercase(strip(ex.msg)))
end
end

@testset "provide a value for `allow_earlier_backwards_compatible_versions`" begin
Expand All @@ -163,25 +160,24 @@ const test_package_parent_dir = joinpath(
) == nothing
)
@test_throws(
Pkg.Types.PkgError(expected_message_1),
Pkg.Resolve.ResolverError,
Pkg.test(;
force_latest_compatible_version = true,
allow_earlier_backwards_compatible_versions,
),
)
@test_logs(
(:error, expected_message_2),
match_mode=:any,
begin
try
Pkg.test(;
force_latest_compatible_version = true,
allow_earlier_backwards_compatible_versions,
)
catch
end
end,
)
let
ex = try
Pkg.test(;
force_latest_compatible_version = true,
allow_earlier_backwards_compatible_versions,
)
catch ex
ex
end
@test ex isa Pkg.Resolve.ResolverError
@test occursin(lowercase(strip(expected_message_1)), lowercase(strip(ex.msg)))
end
end
end
end
Expand All @@ -205,6 +201,17 @@ const test_package_parent_dir = joinpath(
force_latest_compatible_version,
),
)
let
ex = try
Pkg.test(;
force_latest_compatible_version,
)
catch ex
ex
end
@test ex isa Pkg.Resolve.ResolverError
@test occursin(lowercase(strip(expected_message_1)), lowercase(strip(ex.msg)))
end
end

@testset "provide a value for `allow_earlier_backwards_compatible_versions`" begin
Expand All @@ -216,6 +223,18 @@ const test_package_parent_dir = joinpath(
allow_earlier_backwards_compatible_versions,
),
)
let
ex = try
Pkg.test(;
force_latest_compatible_version,
allow_earlier_backwards_compatible_versions,
)
catch ex
ex
end
@test ex isa Pkg.Resolve.ResolverError
@test occursin(lowercase(strip(expected_message_1)), lowercase(strip(ex.msg)))
end
end
end
end
Expand Down Expand Up @@ -269,7 +288,7 @@ const test_package_parent_dir = joinpath(
) == nothing
)
@test_logs(
(:warn, expected_message_3),
(:warn, expected_message_2),
match_mode=:any,
Pkg.test(;
force_latest_compatible_version = true,
Expand All @@ -286,7 +305,7 @@ const test_package_parent_dir = joinpath(
) == nothing
)
@test_logs(
(:warn, expected_message_3),
(:warn, expected_message_2),
match_mode=:any,
Pkg.test(;
force_latest_compatible_version = true,
Expand Down

0 comments on commit dcf38d4

Please sign in to comment.