Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix sandbox call on 1.11-beta #87

Merged
merged 8 commits into from
Jun 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,17 @@ jobs:
- '1.7'
- '1.8'
- '1.9'
- '1.10'
- '^1.11.0-beta2'
os:
- ubuntu-latest
- windows-latest
- macOS-latest
- macOS-12
arch:
- x64
- x86
exclude:
- os: macOS-latest
- os: macOS-12
arch: x86
steps:
- uses: actions/checkout@v3
Expand Down
4 changes: 3 additions & 1 deletion src/TestEnv.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ elseif VERSION < v"1.8-"
include("julia-1.7/TestEnv.jl")
elseif VERSION < v"1.9-"
include("julia-1.8/TestEnv.jl")
else
elseif VERSION < v"1.11-"
include("julia-1.9/TestEnv.jl")
else
include("julia-1.11/TestEnv.jl")
end

end
13 changes: 13 additions & 0 deletions src/julia-1.11/TestEnv.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Pkg
using Pkg: PackageSpec
using Pkg.Types: Context, ensure_resolved, is_project_uuid, write_env, is_stdlib
using Pkg.Types: Types, projectfile_path, manifestfile_path
using Pkg.Operations: manifest_info, manifest_resolve!, project_deps_resolve!
using Pkg.Operations: project_rel_path, project_resolve!
using Pkg.Operations: sandbox, source_path, sandbox_preserve, abspath!
using Pkg.Operations: gen_target_project, isfixed


include("common.jl")
include("activate_do.jl")
include("activate_set.jl")
21 changes: 21 additions & 0 deletions src/julia-1.11/activate_do.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"""
TestEnv.activate(f, [pkg]; allow_reresolve=true)

Activate the test enviroment of `pkg` (defaults to current enviroment), and run `f()`,
then deactivate the enviroment.
This is not useful for many people: Julia is not really designed to have the enviroment
being changed while you are executing code.
However, this *is* useful for anyone doing something like making a alternative to
`Pkg.test()`.
Indeed this is basically extracted from what `Pkg.test()` does.
"""
function activate(f, pkg::AbstractString=current_pkg_name(); allow_reresolve=true)
ctx, pkgspec = ctx_and_pkgspec(pkg)

test_project_override = maybe_gen_project_override!(ctx, pkgspec)
path = pkgspec.path::String
return sandbox(ctx, pkgspec, joinpath(path, "test"), test_project_override; allow_reresolve) do
flush(stdout)
f()
end
end
75 changes: 75 additions & 0 deletions src/julia-1.11/activate_set.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@

# Originally from Pkg.Operations.sandbox

"""
TestEnv.activate([pkg]; allow_reresolve=true)

Activate the test enviroment of `pkg` (defaults to current enviroment).
"""
function activate(pkg::AbstractString=current_pkg_name(); allow_reresolve=true)
ctx, pkgspec = ctx_and_pkgspec(pkg)
# This needs to be first as `gen_target_project` fixes `pkgspec.path` if it is nothing
sandbox_project_override = maybe_gen_project_override!(ctx, pkgspec)

sandbox_path = joinpath(pkgspec.path::String, "test")
sandbox_project = projectfile_path(sandbox_path)

tmp = mktempdir()
tmp_project = projectfile_path(tmp)
tmp_manifest = manifestfile_path(tmp)

# Copy env info over to temp env
if sandbox_project_override !== nothing
Types.write_project(sandbox_project_override, tmp_project)
elseif isfile(sandbox_project)
cp(sandbox_project, tmp_project)
chmod(tmp_project, 0o600)
end
# create merged manifest
# - copy over active subgraph
# - abspath! to maintain location of all deved nodes
working_manifest = abspath!(ctx.env, sandbox_preserve(ctx.env, pkgspec, tmp_project))

# - copy over fixed subgraphs from test subgraph
# really only need to copy over "special" nodes
sandbox_env = Types.EnvCache(projectfile_path(sandbox_path))
sandbox_manifest = abspath!(sandbox_env, sandbox_env.manifest)
for (name, uuid) in sandbox_env.project.deps
entry = get(sandbox_manifest, uuid, nothing)
if entry !== nothing && isfixed(entry)
subgraph = Pkg.Operations.prune_manifest(sandbox_manifest, [uuid])
for (uuid, entry) in subgraph
if haskey(working_manifest, uuid)
Pkg.Operations.pkgerror("can not merge projects")

Check warning on line 43 in src/julia-1.11/activate_set.jl

View check run for this annotation

Codecov / codecov/patch

src/julia-1.11/activate_set.jl#L40-L43

Added lines #L40 - L43 were not covered by tests
end
working_manifest[uuid] = entry
end

Check warning on line 46 in src/julia-1.11/activate_set.jl

View check run for this annotation

Codecov / codecov/patch

src/julia-1.11/activate_set.jl#L45-L46

Added lines #L45 - L46 were not covered by tests
end
end

Types.write_manifest(working_manifest, tmp_manifest)

Base.ACTIVE_PROJECT[] = tmp_project

temp_ctx = Context()
temp_ctx.env.project.deps[pkgspec.name] = pkgspec.uuid

try
Pkg.resolve(temp_ctx; io=devnull)
@debug "Using _parent_ dep graph"
catch err# TODO
allow_reresolve || rethrow()
@debug err
@warn "Could not use exact versions of packages in manifest, re-resolving"
temp_ctx.env.manifest.deps = Dict(

Check warning on line 64 in src/julia-1.11/activate_set.jl

View check run for this annotation

Codecov / codecov/patch

src/julia-1.11/activate_set.jl#L61-L64

Added lines #L61 - L64 were not covered by tests
uuid => entry for
(uuid, entry) in temp_ctx.env.manifest.deps if isfixed(entry)
)
Pkg.resolve(temp_ctx; io=devnull)
@debug "Using _clean_ dep graph"

Check warning on line 69 in src/julia-1.11/activate_set.jl

View check run for this annotation

Codecov / codecov/patch

src/julia-1.11/activate_set.jl#L68-L69

Added lines #L68 - L69 were not covered by tests
end

write_env(temp_ctx.env; update_undo=false)

return Base.active_project()
end
88 changes: 88 additions & 0 deletions src/julia-1.11/common.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
struct TestEnvError <: Exception
msg::AbstractString
end

function Base.showerror(io::IO, ex::TestEnvError, bt; backtrace=true)
printstyled(io, ex.msg, color=Base.error_color())

Check warning on line 6 in src/julia-1.11/common.jl

View check run for this annotation

Codecov / codecov/patch

src/julia-1.11/common.jl#L5-L6

Added lines #L5 - L6 were not covered by tests
end

function current_pkg_name()
ctx = Context()
ctx.env.pkg === nothing && throw(TestEnvError("trying to activate test environment of an unnamed project"))
return ctx.env.pkg.name

Check warning on line 12 in src/julia-1.11/common.jl

View check run for this annotation

Codecov / codecov/patch

src/julia-1.11/common.jl#L12

Added line #L12 was not covered by tests
end

"""
ctx, pkgspec = ctx_and_pkgspec(pkg::AbstractString)

For a given package name `pkg`, instantiate a `Context` for it, and return that `Context`,
and it's `PackageSpec`.
"""
function ctx_and_pkgspec(pkg::AbstractString)
pkgspec = deepcopy(PackageSpec(pkg))
ctx = Context()
isinstalled!(ctx, pkgspec) || throw(TestEnvError("$pkg not installed 👻"))
Pkg.instantiate(ctx)
return ctx, pkgspec
end

"""
isinstalled!(ctx::Context, pkgspec::Pkg.Types.PackageSpec)

Checks if the package is installed by using `ensure_resolved` from `Pkg/src/Types.jl`.
This function fails if the package is not installed, but here we wrap it in a
try-catch as we may want to test another package after the one that isn't installed.
"""
function isinstalled!(ctx::Context, pkgspec::Pkg.Types.PackageSpec)
project_resolve!(ctx.env, [pkgspec])
project_deps_resolve!(ctx.env, [pkgspec])
manifest_resolve!(ctx.env.manifest, [pkgspec])

try
ensure_resolved(ctx, ctx.env.manifest, [pkgspec])
catch err
err isa MethodError && rethrow()
return false

Check warning on line 45 in src/julia-1.11/common.jl

View check run for this annotation

Codecov / codecov/patch

src/julia-1.11/common.jl#L44-L45

Added lines #L44 - L45 were not covered by tests
end
return true
end


function test_dir_has_project_file(ctx, pkgspec)
test_dir = get_test_dir(ctx, pkgspec)
test_dir === nothing && return false
return isfile(joinpath(test_dir, "Project.toml"))
end

"""
get_test_dir(ctx::Context, pkgspec::Pkg.Types.PackageSpec)

Gets the testfile path of the package. Code for each Julia version mirrors that found
in `Pkg/src/Operations.jl`.
"""
function get_test_dir(ctx::Context, pkgspec::Pkg.Types.PackageSpec)
if is_project_uuid(ctx.env, pkgspec.uuid)
pkgspec.path = dirname(ctx.env.project_file)
pkgspec.version = ctx.env.pkg.version

Check warning on line 66 in src/julia-1.11/common.jl

View check run for this annotation

Codecov / codecov/patch

src/julia-1.11/common.jl#L65-L66

Added lines #L65 - L66 were not covered by tests
else
is_stdlib(pkgspec.uuid::Base.UUID) && return
entry = manifest_info(ctx.env.manifest, pkgspec.uuid)
pkgspec.version = entry.version
pkgspec.tree_hash = entry.tree_hash
pkgspec.repo = entry.repo
pkgspec.path = entry.path
pkgspec.pinned = entry.pinned
pkgspec.path = project_rel_path(ctx.env, source_path(ctx.env.project_file, pkgspec)::String)
end
pkgfilepath = source_path(ctx.env.project_file, pkgspec)::String
return joinpath(pkgfilepath, "test")
end


function maybe_gen_project_override!(ctx, pkgspec)
if !test_dir_has_project_file(ctx, pkgspec)
gen_target_project(ctx, pkgspec, pkgspec.path::String, "test")
else
nothing
end
end
Loading