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

Only upgrade .NET tools when an update is available instead of reinstalling everytime #575

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
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
95 changes: 88 additions & 7 deletions src/steps/generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use tracing::{debug, error};
use crate::command::{CommandExt, Utf8Output};
use crate::execution_context::ExecutionContext;
use crate::executor::ExecutorOutput;
use crate::terminal::{print_separator, shell};
use crate::terminal::{print_separator, shell, print_info};
use crate::utils::{self, check_is_python_2_or_shim, require, require_option, which, PathExt, REQUIRE_SUDO};
use crate::Step;
use crate::HOME_DIR;
Expand Down Expand Up @@ -712,13 +712,94 @@ pub fn run_dotnet_upgrade(ctx: &ExecutionContext) -> Result<()> {

print_separator(".NET");

for package in packages {
let package_name = package.split_whitespace().next().unwrap();
ctx.run_type()
let installed_packages = packages.map(|l| {
let mut iter = l.split_whitespace();
let name = iter.next();
let version = iter.next();
(name.unwrap_or(""), version.unwrap_or(""))
}).filter(|(name, _)| !name.is_empty());

let mut packages: Vec<(&str, &str, &str, bool)> = vec![];

for (name, version) in installed_packages {
let s_output = match ctx.run_type()
.execute(&dotnet)
.args(["tool", "update", package_name, "--global"])
.status_checked()
.with_context(|| format!("Failed to update .NET package {package_name}"))?;
.args(["tool", "search", name])
// dotnet will print a greeting message on its first run, from this question:
// https://stackoverflow.com/q/70493706/14092446
// Setting `DOTNET_NOLOGO` to `true` should disable it
.env("DOTNET_NOLOGO", "true")
.output_checked_utf8() {
Ok(s_output) => Some(s_output),
Err(_) => None,
};

if let Some(s_output) = s_output {
let mut search_packages = s_output
.stdout
.lines()
// Skip the header:
//
// Package ID Latest Version Authors Downloads Verified
// ---------------------------------------------------------------------------------------
//
// One thing to note is that .NET SDK respect locale, which means this
// header can be printed in languages other than English, do NOT use it
// to do any check.
.skip(2)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that dotnet search <PKG> won't output this header if <PKG> is not found:

$ dotnet tool search rust
Could not find any results.

Will this happen if the package has already been installed?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If an installed package listed by dotnet tool list --global is guaranteed to be found with dotnet tool search <PKG>, then we can use the --take 1 option to find the exact match:

$ dotnet tool search dotnet-dump
Package ID                     Latest Version      Authors               Downloads      Verified
------------------------------------------------------------------------------------------------
dotnet-dump                    7.0.447801          Microsoft             21089065
dotnet-gcdump                  7.0.447801          Microsoft             9036932
dumpdiag                       0.10.0              DumpDiag              2427
netspark.tools.dumpconfig      1.0.0               Gennadii Kostrov      286

$ dotnet tool search dotnet-dump --take 1
Package ID       Latest Version      Authors        Downloads      Verified
---------------------------------------------------------------------------
dotnet-dump      7.0.447801          Microsoft      21089065

Copy link
Contributor Author

@thecatcore thecatcore Nov 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like dotnet tool doesn't take into account whether the tool version supports the installed toolchain or not.
For example for package dotnet-ef, a few days ago version 8.0.0 released, dropping support for pre-dotnet-8.0, but the cli still tried installing the update, and it's still showing as latest available using dotnet tool search. Even tho I only had 6.0 and 7.0 installed.

.filter(|line| !line.is_empty())
.peekable();

if search_packages.peek().is_none() {
packages.push((name, version, version, false));
} else {
let search_packages = search_packages.map(|l| {
let mut iter = l.split_whitespace();
let name = iter.next();
let version = iter.next();
(name.unwrap_or(""), version.unwrap_or(""))
}).filter(|(name, _)| !name.is_empty());

let mut found = false;

for (search_name, mut lastest_version) in search_packages {
if search_name == name {
found = true;

if lastest_version.is_empty() {
lastest_version = version;
}

packages.push((name, version, lastest_version, version != lastest_version));
break;
}
}

if !found {
packages.push((name, version, version, false));
}
}
} else {
packages.push((name, version, version, false));
}
}

print_info("Package\tInstalled\tLatest\tNeeds update");

for (name, installed, latest, update) in packages.clone() {
print_info(format!("{}\t{}\t{}\t{}", name, installed, latest, update))
}

print_info("");

for (name, _, latest, update) in packages {
if update {
ctx.run_type()
.execute(&dotnet)
.args(["tool", "update", name, "--global", "--version", latest])
.status_checked()
.with_context(|| format!("Failed to update .NET package {name}"))?;
}
}

Ok(())
Expand Down