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

Prompt the user on a manifest version mismatch #3775

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
21 changes: 21 additions & 0 deletions docs/src/managing-packages.md
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,27 @@ To fix such errors, you have a number of options:
- try fixing the problem yourself.
This becomes easier once you understand `Project.toml` files and how they declare their compatibility requirements. We'll return to this example in [Fixing conflicts](@ref).

## Manifest version mismatches

Manifest files also record the Julia version used to resolve the project
dependencies. As such, using an different Julia version can result in a
different resolved manifest, and using an older Julia version than the one the
manifest was generated with risks using packages incompatible with the current
Julia version.

When Pkg notices that there is a mismatch between the manifest Julia version and
the current Julia version, it will prompt you and ask if you would like to:
- **refresh** the manifest, by removing the current one and regenerating the manifest
- **version** the manifest, by renaming the current one to `(Julia)Manifest-v{major}.{minor}.toml` and regenerating the manifest
- **ignore** the mismatch

The prompt only is shown in interactive sessions. In non-interactive sessions
the default behaviour is to *ignore* the mismatch.

The default behaviour of interactive an non-interactive sessions alike can be
set through the `JULIA_PKG_MANIFEST_MISMATCH_ACTION` environment variable, which
recognises the values: `ask`, `refresh`, `version`, and `ignore`.

## Garbage collecting old, unused packages

As packages are updated and projects are deleted, installed package versions and artifacts that were
Expand Down
73 changes: 72 additions & 1 deletion src/manifest.jl
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,14 @@ function read_manifest(f_or_io::Union{String, IO})
if Base.is_v1_format_manifest(raw)
raw = convert_v1_format_manifest(raw)
end
return Manifest(raw, f_or_io)
if f_or_io isa String
raw = check_manifest_julia_maybe_recreate(raw, f_or_io)
end
if isnothing(raw)
return Manifest()
else
return Manifest(raw, f_or_io)
end
end

function convert_v1_format_manifest(old_raw_manifest::Dict)
Expand All @@ -231,6 +238,70 @@ function convert_v1_format_manifest(old_raw_manifest::Dict)
return new_raw_manifest
end

# This responds based on the envvar JULIA_PKG_MANIFEST_MISMATCH_ACTION, which can be:
# - ask (the interactive default): prompt the user to select one of the following actions.
# - ignore (the noninteractive default): do nothing, try to use the existing manifest.
# - refresh: delete and re-create the Manifest.toml.
# - version: move the existing manifest to Manifest-vX.X.toml, generate a new Manifest.toml
# if a Manifest-vX.X.toml already exists, this behaves like "replace".
function check_manifest_julia_maybe_recreate(raw::Dict{String, Any}, file::String)
julia_version = haskey(raw, "julia_version") ? VersionNumber(raw["julia_version"]::String) : nothing
isnothing(julia_version) ||
(julia_version.major == VERSION.major && julia_version.minor == VERSION.minor) &&
return raw
action_mapping = Dict("ignore" => :ignore, "refresh" => :refresh, "version" => :version)
action = get(action_mapping,
get(ENV, "JULIA_PKG_MANIFEST_MISMATCH_ACTION", "") |> lowercase,
ifelse(isinteractive(), :ask, :ignore))
if action === :ask
action = try_prompt_resolve_manifest_conflict(julia_version)
end
action === :ignore && return raw
if action === :version
versioned_file =
joinpath(dirname(file),
string(ifelse(occursin("Julia", basename(file)), "JuliaManifest", "Manifest"),
"-v$(julia_version.major).$(julia_version.minor).toml"))
if ispath(versioned_file)
rm(file)
else
mv(file, versioned_file)
end
else # must be :refresh
rm(file)
end
return nothing
end

# This arguably should live in `REPLMode/REPLMode.jl`, however that module is
# `include`d after this file, and so we can't reference any functions in it here.
function try_prompt_resolve_manifest_conflict(manifest_ver::VersionNumber)
io = stderr_f()
ans = try
printstyled(io, " │ "; color=:green)
println(io, "The active project's manifest seems to be from an ", ifelse(manifest_ver < VERSION, "earlier", "later"), " version ")
printstyled(io, " │ "; color=:green)
print(io, "of Julia (")
printstyled(io, string(manifest_ver), color=:light_magenta)
print(io, ", this is ")
printstyled(io, string(VERSION), color=:light_magenta)
println(io, "), would you like to (r)efresh the manifest,")
printstyled(io, " │ "; color=:green)
println(io, "move it to a (v)ersioned copy then refresh, or (i)gnore this?")
printstyled(io, " └ "; color=:green)
Base.prompt(stdin, io, "(r/v/i)", default = ifelse(manifest_ver < VERSION, "i", "r"))
catch err
if err isa InterruptException # if ^C is entered
println(io, " i") # Indicate what action will be taken
"i"
else
rethrow()
end
end
answer_mapping = Dict("r" => :refresh, "v" => :version, "i" => :ignore)
get(answer_mapping, ans, :ignore)
end

###########
# WRITING #
###########
Expand Down
Loading