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

status: add icons to indicate package updatability #2906

Merged
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
Pkg v1.8 Release Notes
======================

- New `⌃` and `⌅` indicators beside packages in `pkg> status` that have new versions available.
`⌅` indicates when new versions cannot be installed.
- New `outdated::Bool` kwarg to `Pkg.status` (`--outdated` or `-o` in the REPL mode) to show
information about packages not at the latest version.
- New `compat::Bool` kwarg to `Pkg.status` (`--compat` or `-c` in the REPL mode) to show any [compat]
Expand Down
121 changes: 85 additions & 36 deletions src/Operations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1299,7 +1299,7 @@ function up(ctx::Context, pkgs::Vector{PackageSpec}, level::UpgradeLevel;
new_apply = download_source(ctx)
download_artifacts(ctx.env, julia_version=ctx.julia_version, io=ctx.io)
write_env(ctx.env; skip_writing_project) # write env before building
show_update(ctx.env, ctx.registries; io=ctx.io)
show_update(ctx.env, ctx.registries; io=ctx.io, hidden_upgrades_info = true)
build_versions(ctx, union(new_apply, new_git))
end

Expand Down Expand Up @@ -1855,13 +1855,18 @@ struct PackageStatusData
old::Union{Nothing, PackageSpec}
new::Union{Nothing, PackageSpec}
downloaded::Bool
upgradable::Bool
heldback::Bool
compat_data::Union{Nothing, Tuple{Vector{String}, VersionNumber, VersionNumber}}
changed::Bool
end

function print_status(env::EnvCache, old_env::Union{Nothing,EnvCache}, registries::Vector{Registry.RegistryInstance}, header::Symbol,
uuids::Vector, names::Vector; manifest=true, diff=false, ignore_indent::Bool, outdated::Bool, io::IO)
not_installed_indicator = sprint((io, args) -> printstyled(io, args...; color=:red), "→", context=io)
not_latest_version_indicator = sprint((io, args) -> printstyled(io, args...; color=:yellow), "↓", context=io)
uuids::Vector, names::Vector; manifest=true, diff=false, ignore_indent::Bool, outdated::Bool, io::IO,
mode::PackageMode, hidden_upgrades_info::Bool)
not_installed_indicator = sprint((io, args) -> printstyled(io, args...; color=Base.error_color()), "→", context=io)
upgradable_indicator = sprint((io, args) -> printstyled(io, args...; color=:green), "⌃", context=io)
heldback_indicator = sprint((io, args) -> printstyled(io, args...; color=Base.warn_color()), "⌅", context=io)
filter = !isempty(uuids) || !isempty(names)
# setup
xs = diff_array(old_env, env; manifest=manifest)
Expand All @@ -1871,39 +1876,40 @@ function print_status(env::EnvCache, old_env::Union{Nothing,EnvCache}, registrie
(manifest ? "manifest" : "project") * ")", ignore_indent)
return nothing
end
xs = !diff ? xs : eltype(xs)[(id, old, new) for (id, old, new) in xs if old != new]
if isempty(xs)
no_changes = all(p-> p[2] == p[3], xs)
if no_changes
printpkgstyle(io, Symbol("No Changes"), "to $(pathrepr(manifest ? env.manifest_file : env.project_file))", ignore_indent)
return nothing
end
xs = !filter ? xs : eltype(xs)[(id, old, new) for (id, old, new) in xs if (id in uuids || something(new, old).name in names)]
if isempty(xs)
printpkgstyle(io, Symbol("No Matches"),
"in $(diff ? "diff for " : "")$(pathrepr(manifest ? env.manifest_file : env.project_file))", ignore_indent)
return nothing
else
xs = !filter ? xs : eltype(xs)[(id, old, new) for (id, old, new) in xs if (id in uuids || something(new, old).name in names)]
if isempty(xs)
printpkgstyle(io, Symbol("No Matches"),
"in $(diff ? "diff for " : "")$(pathrepr(manifest ? env.manifest_file : env.project_file))", ignore_indent)
return nothing
end
# main print
printpkgstyle(io, header, pathrepr(manifest ? env.manifest_file : env.project_file), ignore_indent)
# Sort stdlibs and _jlls towards the end in status output
xs = sort!(xs, by = (x -> (is_stdlib(x[1]), endswith(something(x[3], x[2]).name, "_jll"), something(x[3], x[2]).name, x[1])))
end
# main print
printpkgstyle(io, header, pathrepr(manifest ? env.manifest_file : env.project_file), ignore_indent)
# Sort stdlibs and _jlls towards the end in status output
xs = sort!(xs, by = (x -> (is_stdlib(x[1]), endswith(something(x[3], x[2]).name, "_jll"), something(x[3], x[2]).name, x[1])))

all_packages_downloaded = true
no_packages_upgradable = true
no_visible_packages_heldback = true
no_packages_heldback = true
lpadding = 2

package_statuses = PackageStatusData[]
for (uuid, old, new) in xs
if Types.is_project_uuid(env, uuid)
continue
end

latest_version = true
# Outdated info
cinfo = nothing
if outdated
if diff == false && !is_stdlib(new.uuid)
@assert old == nothing
cinfo = status_compat_info(new, env, registries)
if cinfo !== nothing
latest_version = false
end
if !isnothing(new) && !is_stdlib(new.uuid)
cinfo = status_compat_info(new, env, registries)
if cinfo !== nothing
latest_version = false
end
end
# if we are running with outdated, only show packages that are upper bounded
Expand All @@ -1913,15 +1919,44 @@ function print_status(env::EnvCache, old_env::Union{Nothing,EnvCache}, registrie

pkg_downloaded = !is_instantiated(new) || is_package_downloaded(env.project_file, new)

all_packages_downloaded &= pkg_downloaded
push!(package_statuses, PackageStatusData(uuid, old, new, pkg_downloaded, cinfo))
new_ver_avail = !latest_version && !Operations.is_tracking_repo(new) && !Operations.is_tracking_path(new)
pkg_upgradable = new_ver_avail && isempty(cinfo[1])
pkg_heldback = new_ver_avail && !isempty(cinfo[1])

if !pkg_downloaded && (pkg_upgradable || pkg_heldback)
# allow space in the gutter for two icons on a single line
lpadding = 3
end
changed = old != new
all_packages_downloaded &= (!changed || pkg_downloaded)
no_packages_upgradable &= (!changed || !pkg_upgradable)
no_visible_packages_heldback &= (!changed || !pkg_heldback)
no_packages_heldback &= !pkg_heldback
push!(package_statuses, PackageStatusData(uuid, old, new, pkg_downloaded, pkg_upgradable, pkg_heldback, cinfo, changed))
end

for pkg in package_statuses
latest_version = pkg.compat_data === nothing
print(io, pkg.downloaded ? " " : not_installed_indicator)
diff && !pkg.changed && continue # in diff mode don't print packages that didn't change
first_indicator_printed = false
if !pkg.downloaded
print(io, not_installed_indicator)
first_indicator_printed = true
elseif lpadding > 2
print(io, " ")
first_indicator_printed = true
end
if pkg.upgradable
print(io, upgradable_indicator)
elseif pkg.heldback
print(io, heldback_indicator)
elseif lpadding == 2 && !first_indicator_printed
print(io, " ")
end

printstyled(io, " [", string(pkg.uuid)[1:8], "] "; color = :light_black)

diff ? print_diff(io, pkg.old, pkg.new) : print_single(io, pkg.new)

if outdated && !diff && pkg.compat_data !== nothing
packages_holding_back, max_version, max_version_compat = pkg.compat_data
if pkg.new.version !== max_version_compat && max_version_compat != max_version
Expand All @@ -1939,8 +1974,22 @@ function print_status(env::EnvCache, old_env::Union{Nothing,EnvCache}, registrie
println(io)
end

if !all_packages_downloaded
printpkgstyle(io, :Info, "packages marked with $not_installed_indicator not downloaded, use `instantiate` to download", ignore_indent)
if !no_changes && !all_packages_downloaded
printpkgstyle(io, :Info, "Packages marked with $not_installed_indicator are not downloaded, use `instantiate` to download", color=Base.info_color(), ignore_indent)
end
if !outdated && (mode != PKGMODE_COMBINED || (manifest == true))
if !no_packages_upgradable && no_visible_packages_heldback
printpkgstyle(io, :Info, "Packages marked with $upgradable_indicator have new versions available", color=Base.info_color(), ignore_indent)
end
if !no_visible_packages_heldback && no_packages_upgradable
printpkgstyle(io, :Info, "Packages marked with $heldback_indicator have new versions available but cannot be upgraded. To see why use `status --outdated`", color=Base.info_color(), ignore_indent)
end
if !no_visible_packages_heldback && !no_packages_upgradable
printpkgstyle(io, :Info, "Packages marked with $upgradable_indicator and $heldback_indicator have new versions available, but those with $heldback_indicator cannot be upgraded. To see why use `status --outdated`", color=Base.info_color(), ignore_indent)
end
if hidden_upgrades_info && no_visible_packages_heldback && !no_packages_heldback
printpkgstyle(io, :Info, "Some packages have new versions but cannot be upgraded. To see why use `status --outdated`", color=Base.info_color(), ignore_indent)
end
end

return nothing
Expand All @@ -1963,17 +2012,17 @@ function git_head_env(env, project_dir)
end
end

function show_update(env::EnvCache, registries::Vector{Registry.RegistryInstance}; io::IO)
function show_update(env::EnvCache, registries::Vector{Registry.RegistryInstance}; io::IO, hidden_upgrades_info = false)
old_env = EnvCache()
old_env.project = env.original_project
old_env.manifest = env.original_manifest
status(env, registries; header=:Updating, mode=PKGMODE_COMBINED, env_diff=old_env, ignore_indent=false, io=io)
status(env, registries; header=:Updating, mode=PKGMODE_COMBINED, env_diff=old_env, ignore_indent=false, io=io, hidden_upgrades_info)
return nothing
end

function status(env::EnvCache, registries::Vector{Registry.RegistryInstance}, pkgs::Vector{PackageSpec}=PackageSpec[];
header=nothing, mode::PackageMode=PKGMODE_PROJECT, git_diff::Bool=false, env_diff=nothing, ignore_indent=true,
io::IO, outdated::Bool=false)
io::IO, outdated::Bool=false, hidden_upgrades_info::Bool=false)
io == Base.devnull && return
# if a package, print header
if header === nothing && env.pkg !== nothing
Expand All @@ -2000,10 +2049,10 @@ function status(env::EnvCache, registries::Vector{Registry.RegistryInstance}, pk
diff = old_env !== nothing
header = something(header, diff ? :Diff : :Status)
if mode == PKGMODE_PROJECT || mode == PKGMODE_COMBINED
print_status(env, old_env, registries, header, filter_uuids, filter_names; manifest=false, diff, ignore_indent, io, outdated)
print_status(env, old_env, registries, header, filter_uuids, filter_names; manifest=false, diff, ignore_indent, io, outdated, mode, hidden_upgrades_info)
end
if mode == PKGMODE_MANIFEST || mode == PKGMODE_COMBINED
print_status(env, old_env, registries, header, filter_uuids, filter_names; diff, ignore_indent, io, outdated)
print_status(env, old_env, registries, header, filter_uuids, filter_names; diff, ignore_indent, io, outdated, mode, hidden_upgrades_info)
end
if is_manifest_current(env) === false
printpkgstyle(io, :Warning, """The project dependencies or compat requirements have changed since the manifest was last resolved. \
Expand Down
26 changes: 16 additions & 10 deletions src/Pkg.jl
Original file line number Diff line number Diff line change
Expand Up @@ -394,13 +394,9 @@ const resolve = API.resolve
Pkg.status([pkgs...]; mode::PackageMode=PKGMODE_PROJECT, diff::Bool=false, compat::Bool=false, io::IO=stdout)

Print out the status of the project/manifest.
If `mode` is `PKGMODE_PROJECT`, print out status only about the packages
that are in the project (explicitly added). If `mode` is `PKGMODE_MANIFEST`,
print status also about those in the manifest (recursive dependencies). If there are
any packages listed as arguments, the output will be limited to those packages.

Setting `diff=true` will, if the environment is in a git repository, limit
the output to the difference as compared to the last git commit.
Packages marked with `⌃` have new versions that can be installed, e.g. via [`Pkg.up`](@ref).
Those marked with `⌅` have new versions available, but that cannot be installed. To see why use the `outdated` kwarg.

Setting `outdated=true` will only show packages that are not on the latest version,
their maximum version and why they are not on the latest version (either due to other
Expand All @@ -409,9 +405,9 @@ As an example, a status output like:
```
pkg> Pkg.status(; outdated=true)
Status `Manifest.toml`
[a8cc5b0e] Crayons v2.0.0 [<v3.0.0], (<v4.0.4)
[b8a86587] NearestNeighbors v0.4.8 (<v0.4.9) [compat]
[2ab3a3ac] LogExpFunctions v0.2.5 (<v0.3.0): SpecialFunctions
[a8cc5b0e] Crayons v2.0.0 [<v3.0.0], (<v4.0.4)
[b8a86587] NearestNeighbors v0.4.8 (<v0.4.9) [compat]
[2ab3a3ac] LogExpFunctions v0.2.5 (<v0.3.0): SpecialFunctions
```
means that the latest version of Crayons is 4.0.4 but the latest version compatible
with the `[compat]` section in the current project is 3.0.0.
Expand All @@ -420,6 +416,14 @@ it is held back to 0.4.8.
The latest version of LogExpFunctions is 0.3.0 but SpecialFunctions
is holding it back to 0.2.5.

If `mode` is `PKGMODE_PROJECT`, print out status only about the packages
that are in the project (explicitly added). If `mode` is `PKGMODE_MANIFEST`,
print status also about those in the manifest (recursive dependencies). If there are
any packages listed as arguments, the output will be limited to those packages.

Setting `diff=true` will, if the environment is in a git repository, limit
the output to the difference as compared to the last git commit.

See [`Pkg.project`](@ref) and [`Pkg.dependencies`](@ref) to get the project/manifest
status as a Julia object instead of printing it.

Expand All @@ -431,7 +435,9 @@ status as a Julia object instead of printing it.
is the default for environments in git repositories.

!!! compat "Julia 1.8"
The `outdated` keyword argument reguires at least Julia 1.8
The `⌃` and `⌅` indicators were added in Julia 1.8.
The `outdated` keyword argument requires at least Julia 1.8.

"""
const status = API.status

Expand Down
14 changes: 9 additions & 5 deletions src/REPLMode/command_declarations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -364,14 +364,17 @@ PSA[:name => "status",
[st|status] [-d|--diff] [-o|--outdated] [-m|--manifest] [pkgs...]
[st|status] [-c|--compat] [pkgs...]

Show the status of the current environment. In `--project` mode (default), the
status of the project file is summarized. In `--manifest` mode the output also
includes the recursive dependencies of added packages given in the manifest.
Show the status of the current environment.
Packages marked with `⌃` have new versions that can be installed, e.g. via `pkg> up`.
Those marked with `⌅` have new versions available, but that cannot be installed. To see why
use `pkg> status --outdated` which shows any packages that are not at their latest version
and if any packages are holding them back.

In `--project` mode (default), the status of the project file is summarized. In `--manifest`
mode the output also includes the recursive dependencies of added packages given in the manifest.
If there are any packages listed as arguments the output will be limited to those packages.
The `--diff` option will, if the environment is in a git repository, limit
the output to the difference as compared to the last git commit.
The `--outdated` option in addition show if some packages are not at their latest version
and what packages are holding them back.
The `--compat` option alone shows project compat entries.

!!! compat "Julia 1.1"
Expand All @@ -382,6 +385,7 @@ The `--compat` option alone shows project compat entries.
is the default for environments in git repositories.

!!! compat "Julia 1.8"
The `⌃` and `⌅` indicators were added in Julia 1.8
The `--outdated` and `--compat` options require at least Julia 1.8.
""",
],
Expand Down
Loading