2021-04-11 01:09:09 +03:00
|
|
|
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
|
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
|
2021-03-26 04:19:32 +03:00
|
|
|
use crate::helpers::{
|
|
|
|
app_paths::{app_dir, tauri_dir},
|
|
|
|
config::get as get_config,
|
2021-06-01 01:37:53 +03:00
|
|
|
framework::infer_from_package_json as infer_framework,
|
2021-03-26 04:19:32 +03:00
|
|
|
};
|
|
|
|
use serde::Deserialize;
|
|
|
|
|
|
|
|
use std::{
|
|
|
|
collections::HashMap,
|
|
|
|
fs::{read_dir, read_to_string},
|
|
|
|
panic,
|
|
|
|
path::{Path, PathBuf},
|
|
|
|
process::Command,
|
|
|
|
};
|
|
|
|
|
|
|
|
#[derive(Deserialize)]
|
|
|
|
struct YarnVersionInfo {
|
|
|
|
data: Vec<String>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Deserialize)]
|
|
|
|
struct CargoLockPackage {
|
|
|
|
name: String,
|
|
|
|
version: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Deserialize)]
|
|
|
|
struct CargoLock {
|
|
|
|
package: Vec<CargoLockPackage>,
|
|
|
|
}
|
|
|
|
|
2021-04-14 19:37:29 +03:00
|
|
|
#[derive(Deserialize)]
|
|
|
|
struct JsCliVersionMetadata {
|
|
|
|
version: String,
|
|
|
|
node: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Deserialize)]
|
2021-04-15 08:48:38 +03:00
|
|
|
#[serde(rename_all = "camelCase")]
|
2021-04-14 19:37:29 +03:00
|
|
|
struct VersionMetadata {
|
2021-04-17 01:52:20 +03:00
|
|
|
#[serde(rename = "cli.js")]
|
2021-04-14 19:37:29 +03:00
|
|
|
js_cli: JsCliVersionMetadata,
|
|
|
|
}
|
|
|
|
|
2021-03-26 04:19:32 +03:00
|
|
|
#[derive(Clone, Deserialize)]
|
|
|
|
struct CargoManifestDependencyPackage {
|
|
|
|
version: Option<String>,
|
|
|
|
path: Option<PathBuf>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Deserialize)]
|
|
|
|
#[serde(untagged)]
|
|
|
|
enum CargoManifestDependency {
|
|
|
|
Version(String),
|
|
|
|
Package(CargoManifestDependencyPackage),
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Deserialize)]
|
|
|
|
struct CargoManifestPackage {
|
|
|
|
version: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Deserialize)]
|
|
|
|
struct CargoManifest {
|
|
|
|
package: CargoManifestPackage,
|
|
|
|
dependencies: HashMap<String, CargoManifestDependency>,
|
|
|
|
}
|
|
|
|
|
2021-08-24 19:01:16 +03:00
|
|
|
enum PackageManager {
|
|
|
|
Npm,
|
|
|
|
Pnpm,
|
|
|
|
Yarn,
|
|
|
|
}
|
|
|
|
|
2021-01-30 18:15:47 +03:00
|
|
|
#[derive(Default)]
|
|
|
|
pub struct Info;
|
|
|
|
|
2021-03-26 04:19:32 +03:00
|
|
|
fn crate_latest_version(name: &str) -> Option<String> {
|
|
|
|
let url = format!("https://docs.rs/crate/{}/", name);
|
|
|
|
match ureq::get(&url).call() {
|
|
|
|
Ok(response) => match (response.status(), response.header("location")) {
|
|
|
|
(302, Some(location)) => Some(location.replace(&url, "")),
|
|
|
|
_ => None,
|
|
|
|
},
|
|
|
|
Err(_) => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-24 19:01:16 +03:00
|
|
|
fn npm_latest_version(pm: &PackageManager, name: &str) -> crate::Result<Option<String>> {
|
2021-04-22 23:34:46 +03:00
|
|
|
let mut cmd;
|
2021-08-24 19:01:16 +03:00
|
|
|
match pm {
|
|
|
|
PackageManager::Yarn => {
|
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
{
|
|
|
|
cmd = Command::new("cmd");
|
|
|
|
cmd.arg("/c").arg("yarn");
|
|
|
|
}
|
2021-04-17 01:52:20 +03:00
|
|
|
|
2021-08-24 19:01:16 +03:00
|
|
|
#[cfg(not(target_os = "windows"))]
|
|
|
|
{
|
|
|
|
cmd = Command::new("yarn")
|
|
|
|
}
|
2021-04-17 01:52:20 +03:00
|
|
|
|
2021-08-24 19:01:16 +03:00
|
|
|
let output = cmd
|
|
|
|
.arg("info")
|
|
|
|
.arg(name)
|
|
|
|
.args(&["version", "--json"])
|
|
|
|
.output()?;
|
|
|
|
if output.status.success() {
|
|
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
|
|
let info: YarnVersionInfo = serde_json::from_str(&stdout)?;
|
|
|
|
Ok(Some(info.data.last().unwrap().to_string()))
|
|
|
|
} else {
|
|
|
|
Ok(None)
|
|
|
|
}
|
2021-04-17 01:52:20 +03:00
|
|
|
}
|
2021-08-24 19:01:16 +03:00
|
|
|
PackageManager::Npm => {
|
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
{
|
|
|
|
cmd = Command::new("cmd");
|
|
|
|
cmd.arg("/c").arg("npm");
|
|
|
|
}
|
2021-04-17 01:52:20 +03:00
|
|
|
|
2021-08-24 19:01:16 +03:00
|
|
|
#[cfg(not(target_os = "windows"))]
|
|
|
|
{
|
|
|
|
cmd = Command::new("npm")
|
|
|
|
}
|
|
|
|
|
|
|
|
let output = cmd.arg("show").arg(name).arg("version").output()?;
|
|
|
|
if output.status.success() {
|
|
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
|
|
Ok(Some(stdout.replace("\n", "")))
|
|
|
|
} else {
|
|
|
|
Ok(None)
|
|
|
|
}
|
2021-04-17 01:52:20 +03:00
|
|
|
}
|
2021-08-24 19:01:16 +03:00
|
|
|
PackageManager::Pnpm => {
|
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
{
|
|
|
|
cmd = Command::new("cmd");
|
|
|
|
cmd.arg("/c").arg("pnpm");
|
|
|
|
}
|
2021-04-17 01:52:20 +03:00
|
|
|
|
2021-08-24 19:01:16 +03:00
|
|
|
#[cfg(not(target_os = "windows"))]
|
|
|
|
{
|
|
|
|
cmd = Command::new("pnpm")
|
|
|
|
}
|
|
|
|
|
|
|
|
let output = cmd.arg("info").arg(name).arg("version").output()?;
|
|
|
|
if output.status.success() {
|
|
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
|
|
Ok(Some(stdout.replace("\n", "")))
|
|
|
|
} else {
|
|
|
|
Ok(None)
|
|
|
|
}
|
2021-03-26 04:19:32 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn npm_package_version<P: AsRef<Path>>(
|
2021-08-24 19:01:16 +03:00
|
|
|
pm: &PackageManager,
|
2021-03-26 04:19:32 +03:00
|
|
|
name: &str,
|
|
|
|
app_dir: P,
|
|
|
|
) -> crate::Result<Option<String>> {
|
2021-04-22 23:34:46 +03:00
|
|
|
let mut cmd;
|
2021-08-24 19:01:16 +03:00
|
|
|
let output = match pm {
|
|
|
|
PackageManager::Yarn => {
|
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
{
|
|
|
|
cmd = Command::new("cmd");
|
|
|
|
cmd.arg("/c").arg("yarn");
|
|
|
|
}
|
2021-04-17 01:52:20 +03:00
|
|
|
|
2021-08-24 19:01:16 +03:00
|
|
|
#[cfg(not(target_os = "windows"))]
|
|
|
|
{
|
|
|
|
cmd = Command::new("yarn")
|
|
|
|
}
|
2021-04-17 01:52:20 +03:00
|
|
|
|
2021-08-24 19:01:16 +03:00
|
|
|
cmd
|
|
|
|
.args(&["list", "--pattern"])
|
|
|
|
.arg(name)
|
|
|
|
.args(&["--depth", "0"])
|
|
|
|
.current_dir(app_dir)
|
|
|
|
.output()?
|
2021-04-17 01:52:20 +03:00
|
|
|
}
|
2021-08-24 19:01:16 +03:00
|
|
|
PackageManager::Npm => {
|
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
{
|
|
|
|
cmd = Command::new("cmd");
|
|
|
|
cmd.arg("/c").arg("npm");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(target_os = "windows"))]
|
|
|
|
{
|
|
|
|
cmd = Command::new("npm")
|
|
|
|
}
|
2021-04-17 01:52:20 +03:00
|
|
|
|
2021-08-24 19:01:16 +03:00
|
|
|
cmd
|
|
|
|
.arg("list")
|
|
|
|
.arg(name)
|
|
|
|
.args(&["version", "--depth", "0"])
|
|
|
|
.current_dir(app_dir)
|
|
|
|
.output()?
|
2021-04-17 01:52:20 +03:00
|
|
|
}
|
2021-08-24 19:01:16 +03:00
|
|
|
PackageManager::Pnpm => {
|
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
{
|
|
|
|
cmd = Command::new("cmd");
|
|
|
|
cmd.arg("/c").arg("pnpm");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(target_os = "windows"))]
|
|
|
|
{
|
|
|
|
cmd = Command::new("pnpm")
|
|
|
|
}
|
2021-04-17 01:52:20 +03:00
|
|
|
|
2021-08-24 19:01:16 +03:00
|
|
|
cmd
|
|
|
|
.arg("list")
|
|
|
|
.arg(name)
|
|
|
|
.args(&["--parseable", "--depth", "0"])
|
|
|
|
.current_dir(app_dir)
|
|
|
|
.output()?
|
|
|
|
}
|
2021-03-26 04:19:32 +03:00
|
|
|
};
|
|
|
|
if output.status.success() {
|
|
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
2021-04-23 01:14:15 +03:00
|
|
|
let regex = regex::Regex::new("@([\\da-zA-Z\\-\\.]+)").unwrap();
|
2021-03-26 04:19:32 +03:00
|
|
|
Ok(
|
|
|
|
regex
|
|
|
|
.captures_iter(&stdout)
|
|
|
|
.last()
|
|
|
|
.and_then(|cap| cap.get(1).map(|v| v.as_str().to_string())),
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_version(command: &str, args: &[&str]) -> crate::Result<Option<String>> {
|
2021-04-17 01:52:20 +03:00
|
|
|
let mut cmd;
|
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
{
|
|
|
|
cmd = Command::new("cmd");
|
|
|
|
cmd.arg("/c").arg(command);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(target_os = "windows"))]
|
|
|
|
{
|
|
|
|
cmd = Command::new(command)
|
|
|
|
}
|
|
|
|
|
|
|
|
let output = cmd.args(args).arg("--version").output()?;
|
2021-03-26 04:19:32 +03:00
|
|
|
let version = if output.status.success() {
|
2021-06-05 15:23:09 +03:00
|
|
|
Some(
|
|
|
|
String::from_utf8_lossy(&output.stdout)
|
|
|
|
.replace("\n", "")
|
|
|
|
.replace("\r", ""),
|
|
|
|
)
|
2021-03-26 04:19:32 +03:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
Ok(version)
|
|
|
|
}
|
|
|
|
|
2021-05-04 07:26:56 +03:00
|
|
|
#[cfg(windows)]
|
|
|
|
fn webview2_version() -> crate::Result<Option<String>> {
|
|
|
|
let output = Command::new("powershell")
|
|
|
|
.args(&["-NoProfile", "-Command"])
|
|
|
|
.arg("Get-ItemProperty -Path 'HKLM:\\SOFTWARE\\WOW6432Node\\Microsoft\\EdgeUpdate\\Clients\\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}' | ForEach-Object {$_.pv}")
|
|
|
|
.output()?;
|
|
|
|
let version = if output.status.success() {
|
|
|
|
Some(String::from_utf8_lossy(&output.stdout).replace("\n", ""))
|
|
|
|
} else {
|
|
|
|
// check 32bit installation
|
|
|
|
let output = Command::new("powershell")
|
|
|
|
.args(&["-NoProfile", "-Command"])
|
|
|
|
.arg("Get-ItemProperty -Path 'HKLM:\\SOFTWARE\\Microsoft\\EdgeUpdate\\Clients\\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}' | ForEach-Object {$_.pv}")
|
|
|
|
.output()?;
|
|
|
|
if output.status.success() {
|
|
|
|
Some(String::from_utf8_lossy(&output.stdout).replace("\n", ""))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
};
|
|
|
|
Ok(version)
|
|
|
|
}
|
|
|
|
|
2021-09-21 16:24:48 +03:00
|
|
|
#[cfg(windows)]
|
|
|
|
fn run_vs_setup_instance() -> std::io::Result<std::process::Output> {
|
|
|
|
Command::new("powershell")
|
|
|
|
.args(&["-NoProfile", "-Command"])
|
|
|
|
.arg("Get-VSSetupInstance")
|
|
|
|
.output()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
fn build_tools_version() -> crate::Result<Option<Vec<String>>> {
|
|
|
|
let mut output = run_vs_setup_instance();
|
|
|
|
if output.is_err() {
|
|
|
|
Command::new("powershell")
|
|
|
|
.args(&["-NoProfile", "-Command"])
|
|
|
|
.arg("Install-Module VSSetup -Scope CurrentUser")
|
|
|
|
.output()?;
|
|
|
|
output = run_vs_setup_instance();
|
|
|
|
}
|
|
|
|
let output = output?;
|
|
|
|
let versions = if output.status.success() {
|
|
|
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
|
|
|
let mut versions = Vec::new();
|
|
|
|
|
|
|
|
let regex = regex::Regex::new(r"Visual Studio Build Tools (?P<version>\d+)").unwrap();
|
|
|
|
for caps in regex.captures_iter(&stdout) {
|
|
|
|
versions.push(caps["version"].to_string());
|
|
|
|
}
|
|
|
|
|
|
|
|
if versions.is_empty() {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(versions)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
Ok(versions)
|
|
|
|
}
|
|
|
|
|
2021-03-26 04:19:32 +03:00
|
|
|
struct InfoBlock {
|
|
|
|
section: bool,
|
|
|
|
key: &'static str,
|
|
|
|
value: Option<String>,
|
|
|
|
suffix: Option<String>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl InfoBlock {
|
|
|
|
fn new(key: &'static str) -> Self {
|
|
|
|
Self {
|
|
|
|
section: false,
|
|
|
|
key,
|
|
|
|
value: None,
|
|
|
|
suffix: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn section(mut self) -> Self {
|
|
|
|
self.section = true;
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
fn value<V: Into<Option<String>>>(mut self, value: V) -> Self {
|
|
|
|
self.value = value.into();
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
fn suffix<S: Into<Option<String>>>(mut self, suffix: S) -> Self {
|
|
|
|
self.suffix = suffix.into();
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
fn display(&self) {
|
|
|
|
if self.section {
|
|
|
|
println!();
|
|
|
|
}
|
|
|
|
print!("{}", self.key);
|
|
|
|
if let Some(value) = &self.value {
|
|
|
|
print!(" - {}", value);
|
|
|
|
}
|
|
|
|
if let Some(suffix) = &self.suffix {
|
|
|
|
print!("{}", suffix);
|
|
|
|
}
|
|
|
|
println!();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct VersionBlock {
|
|
|
|
section: bool,
|
|
|
|
key: &'static str,
|
|
|
|
version: Option<String>,
|
|
|
|
target_version: Option<String>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl VersionBlock {
|
|
|
|
fn new<V: Into<Option<String>>>(key: &'static str, version: V) -> Self {
|
|
|
|
Self {
|
|
|
|
section: false,
|
|
|
|
key,
|
|
|
|
version: version.into(),
|
|
|
|
target_version: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn target_version<V: Into<Option<String>>>(mut self, version: V) -> Self {
|
|
|
|
self.target_version = version.into();
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
fn display(&self) {
|
|
|
|
if self.section {
|
|
|
|
println!();
|
|
|
|
}
|
|
|
|
print!("{}", self.key);
|
|
|
|
if let Some(version) = &self.version {
|
|
|
|
print!(" - {}", version);
|
|
|
|
} else {
|
2021-05-04 07:26:56 +03:00
|
|
|
print!(" - Not installed");
|
2021-03-26 04:19:32 +03:00
|
|
|
}
|
|
|
|
if let (Some(version), Some(target_version)) = (&self.version, &self.target_version) {
|
|
|
|
let version = semver::Version::parse(version).unwrap();
|
|
|
|
let target_version = semver::Version::parse(target_version).unwrap();
|
|
|
|
if version < target_version {
|
|
|
|
print!(" (outdated, latest: {})", target_version);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
println!();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-30 18:15:47 +03:00
|
|
|
impl Info {
|
|
|
|
pub fn new() -> Self {
|
|
|
|
Default::default()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn run(self) -> crate::Result<()> {
|
2021-03-26 04:19:32 +03:00
|
|
|
let os_info = os_info::get();
|
|
|
|
InfoBlock {
|
|
|
|
section: true,
|
|
|
|
key: "Operating System",
|
|
|
|
value: Some(format!(
|
|
|
|
"{}, version {} {:?}",
|
|
|
|
os_info.os_type(),
|
|
|
|
os_info.version(),
|
|
|
|
os_info.bitness()
|
|
|
|
)),
|
|
|
|
suffix: None,
|
|
|
|
}
|
|
|
|
.display();
|
|
|
|
|
2021-05-04 07:26:56 +03:00
|
|
|
#[cfg(windows)]
|
|
|
|
VersionBlock::new("Webview2", webview2_version().unwrap_or_default()).display();
|
2021-09-21 16:24:48 +03:00
|
|
|
#[cfg(windows)]
|
|
|
|
VersionBlock::new(
|
|
|
|
"Visual Studio Build Tools",
|
|
|
|
build_tools_version()
|
|
|
|
.map(|r| {
|
|
|
|
let required_string = "(>= 2019 required)";
|
|
|
|
let multiple_string =
|
|
|
|
"(multiple versions might conflict; keep only 2019 if build errors occur)";
|
|
|
|
r.map(|v| match v.len() {
|
|
|
|
1 if v[0].as_str() < "2019" => format!("{} {}", v[0], required_string),
|
|
|
|
1 if v[0].as_str() >= "2019" => v[0].clone(),
|
|
|
|
_ if v.contains(&"2019".into()) => format!("{} {}", v.join(", "), multiple_string),
|
|
|
|
_ => format!("{} {} {}", v.join(", "), required_string, multiple_string),
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.unwrap_or_default(),
|
|
|
|
)
|
|
|
|
.display();
|
2021-05-04 07:26:56 +03:00
|
|
|
|
2021-03-26 04:19:32 +03:00
|
|
|
let hook = panic::take_hook();
|
|
|
|
panic::set_hook(Box::new(|_info| {
|
|
|
|
// do nothing
|
|
|
|
}));
|
|
|
|
let app_dir = panic::catch_unwind(app_dir).map(Some).unwrap_or_default();
|
|
|
|
panic::set_hook(hook);
|
|
|
|
|
2021-08-24 19:01:16 +03:00
|
|
|
let mut package_manager = PackageManager::Npm;
|
|
|
|
if let Some(app_dir) = &app_dir {
|
|
|
|
let file_names = read_dir(app_dir)
|
|
|
|
.unwrap()
|
|
|
|
.filter(|e| {
|
|
|
|
e.as_ref()
|
|
|
|
.unwrap()
|
|
|
|
.metadata()
|
|
|
|
.unwrap()
|
|
|
|
.file_type()
|
|
|
|
.is_file()
|
|
|
|
})
|
|
|
|
.map(|e| e.unwrap().file_name().to_string_lossy().into_owned())
|
|
|
|
.collect::<Vec<String>>();
|
|
|
|
package_manager = get_package_manager(&file_names)?;
|
|
|
|
}
|
2021-03-26 04:19:32 +03:00
|
|
|
|
|
|
|
if let Some(node_version) = get_version("node", &[]).unwrap_or_default() {
|
|
|
|
InfoBlock::new("Node.js environment").section().display();
|
2021-04-14 19:37:29 +03:00
|
|
|
let metadata = serde_json::from_str::<VersionMetadata>(include_str!("../metadata.json"))?;
|
2021-03-26 04:19:32 +03:00
|
|
|
VersionBlock::new(
|
|
|
|
" Node.js",
|
|
|
|
node_version.chars().skip(1).collect::<String>(),
|
|
|
|
)
|
2021-04-14 19:37:29 +03:00
|
|
|
.target_version(metadata.js_cli.node.replace(">= ", ""))
|
2021-03-26 04:19:32 +03:00
|
|
|
.display();
|
|
|
|
|
2021-04-14 19:37:29 +03:00
|
|
|
VersionBlock::new(" @tauri-apps/cli", metadata.js_cli.version)
|
2021-08-24 19:01:16 +03:00
|
|
|
.target_version(npm_latest_version(&package_manager, "@tauri-apps/cli").unwrap_or_default())
|
2021-03-26 04:19:32 +03:00
|
|
|
.display();
|
|
|
|
if let Some(app_dir) = &app_dir {
|
|
|
|
VersionBlock::new(
|
|
|
|
" @tauri-apps/api",
|
2021-08-24 19:01:16 +03:00
|
|
|
npm_package_version(&package_manager, "@tauri-apps/api", app_dir).unwrap_or_default(),
|
2021-03-26 04:19:32 +03:00
|
|
|
)
|
2021-08-24 19:01:16 +03:00
|
|
|
.target_version(npm_latest_version(&package_manager, "@tauri-apps/api").unwrap_or_default())
|
2021-03-26 04:19:32 +03:00
|
|
|
.display();
|
|
|
|
}
|
|
|
|
|
|
|
|
InfoBlock::new("Global packages").section().display();
|
|
|
|
|
|
|
|
VersionBlock::new(" npm", get_version("npm", &[]).unwrap_or_default()).display();
|
2021-08-24 19:01:16 +03:00
|
|
|
VersionBlock::new(" pnpm", get_version("pnpm", &[]).unwrap_or_default()).display();
|
2021-03-26 04:19:32 +03:00
|
|
|
VersionBlock::new(" yarn", get_version("yarn", &[]).unwrap_or_default()).display();
|
|
|
|
}
|
|
|
|
|
|
|
|
InfoBlock::new("Rust environment").section().display();
|
|
|
|
VersionBlock::new(
|
|
|
|
" rustc",
|
|
|
|
get_version("rustc", &[]).unwrap_or_default().map(|v| {
|
|
|
|
let mut s = v.split(' ');
|
|
|
|
s.next();
|
|
|
|
s.next().unwrap().to_string()
|
|
|
|
}),
|
|
|
|
)
|
|
|
|
.display();
|
|
|
|
VersionBlock::new(
|
|
|
|
" cargo",
|
|
|
|
get_version("cargo", &[]).unwrap_or_default().map(|v| {
|
|
|
|
let mut s = v.split(' ');
|
|
|
|
s.next();
|
|
|
|
s.next().unwrap().to_string()
|
|
|
|
}),
|
|
|
|
)
|
|
|
|
.display();
|
|
|
|
|
|
|
|
if let Some(app_dir) = app_dir {
|
|
|
|
InfoBlock::new("App directory structure")
|
|
|
|
.section()
|
|
|
|
.display();
|
|
|
|
for entry in read_dir(app_dir)? {
|
|
|
|
let entry = entry?;
|
|
|
|
if entry.path().is_dir() {
|
|
|
|
println!("/{}", entry.path().file_name().unwrap().to_string_lossy());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
InfoBlock::new("App").section().display();
|
|
|
|
let tauri_dir = tauri_dir();
|
|
|
|
let manifest: Option<CargoManifest> =
|
|
|
|
if let Ok(manifest_contents) = read_to_string(tauri_dir.join("Cargo.toml")) {
|
|
|
|
toml::from_str(&manifest_contents).ok()
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
let lock: Option<CargoLock> =
|
|
|
|
if let Ok(lock_contents) = read_to_string(tauri_dir.join("Cargo.lock")) {
|
|
|
|
toml::from_str(&lock_contents).ok()
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
let tauri_lock_packages: Vec<CargoLockPackage> = lock
|
|
|
|
.as_ref()
|
|
|
|
.map(|lock| {
|
|
|
|
lock
|
|
|
|
.package
|
|
|
|
.iter()
|
|
|
|
.filter(|p| p.name == "tauri")
|
|
|
|
.cloned()
|
|
|
|
.collect()
|
|
|
|
})
|
|
|
|
.unwrap_or_default();
|
|
|
|
let (tauri_version_string, found_tauri_versions) =
|
|
|
|
match (&manifest, &lock, tauri_lock_packages.len()) {
|
|
|
|
(Some(_manifest), Some(_lock), 1) => {
|
|
|
|
let tauri_lock_package = tauri_lock_packages.first().unwrap();
|
|
|
|
(
|
|
|
|
tauri_lock_package.version.clone(),
|
|
|
|
vec![tauri_lock_package.version.clone()],
|
|
|
|
)
|
|
|
|
}
|
|
|
|
(None, Some(_lock), 1) => {
|
|
|
|
let tauri_lock_package = tauri_lock_packages.first().unwrap();
|
|
|
|
(
|
|
|
|
format!("{} (no manifest)", tauri_lock_package.version),
|
|
|
|
vec![tauri_lock_package.version.clone()],
|
|
|
|
)
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
let mut found_tauri_versions = Vec::new();
|
|
|
|
let manifest_version = match manifest.and_then(|m| m.dependencies.get("tauri").cloned())
|
|
|
|
{
|
|
|
|
Some(tauri) => match tauri {
|
|
|
|
CargoManifestDependency::Version(v) => {
|
|
|
|
found_tauri_versions.push(v.clone());
|
|
|
|
v
|
|
|
|
}
|
|
|
|
CargoManifestDependency::Package(p) => {
|
|
|
|
if let Some(v) = p.version {
|
|
|
|
found_tauri_versions.push(v.clone());
|
|
|
|
v
|
|
|
|
} else if let Some(p) = p.path {
|
|
|
|
let manifest_path = tauri_dir.join(&p).join("Cargo.toml");
|
|
|
|
let v = match read_to_string(&manifest_path)
|
|
|
|
.map_err(|_| ())
|
|
|
|
.and_then(|m| toml::from_str::<CargoManifest>(&m).map_err(|_| ()))
|
|
|
|
{
|
|
|
|
Ok(manifest) => manifest.package.version,
|
|
|
|
Err(_) => "unknown version".to_string(),
|
|
|
|
};
|
|
|
|
format!("path:{:?} [{}]", p, v)
|
|
|
|
} else {
|
|
|
|
"unknown manifest".to_string()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
None => "no manifest".to_string(),
|
|
|
|
};
|
|
|
|
|
|
|
|
let lock_version = match (lock, tauri_lock_packages.is_empty()) {
|
|
|
|
(Some(_lock), true) => tauri_lock_packages
|
|
|
|
.iter()
|
|
|
|
.map(|p| p.version.clone())
|
|
|
|
.collect::<Vec<String>>()
|
|
|
|
.join(", "),
|
|
|
|
(Some(_lock), false) => "unknown lockfile".to_string(),
|
|
|
|
_ => "no lockfile".to_string(),
|
|
|
|
};
|
|
|
|
|
|
|
|
(
|
|
|
|
format!("{} ({})", manifest_version, lock_version),
|
|
|
|
found_tauri_versions,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let tauri_version = found_tauri_versions
|
|
|
|
.into_iter()
|
|
|
|
.map(|v| semver::Version::parse(&v).unwrap())
|
|
|
|
.max();
|
|
|
|
let suffix = match (tauri_version, crate_latest_version("tauri")) {
|
|
|
|
(Some(version), Some(target_version)) => {
|
|
|
|
let target_version = semver::Version::parse(&target_version).unwrap();
|
|
|
|
if version < target_version {
|
|
|
|
Some(format!(" (outdated, latest: {})", target_version))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => None,
|
|
|
|
};
|
|
|
|
InfoBlock::new(" tauri.rs")
|
|
|
|
.value(tauri_version_string)
|
|
|
|
.suffix(suffix)
|
|
|
|
.display();
|
|
|
|
|
|
|
|
if let Ok(config) = get_config(None) {
|
|
|
|
let config_guard = config.lock().unwrap();
|
|
|
|
let config = config_guard.as_ref().unwrap();
|
2021-05-31 19:33:44 +03:00
|
|
|
InfoBlock::new(" build-type")
|
2021-03-26 04:19:32 +03:00
|
|
|
.value(if config.tauri.bundle.active {
|
|
|
|
"bundle".to_string()
|
|
|
|
} else {
|
|
|
|
"build".to_string()
|
|
|
|
})
|
|
|
|
.display();
|
2021-05-31 19:33:44 +03:00
|
|
|
InfoBlock::new(" CSP")
|
2021-03-26 04:19:32 +03:00
|
|
|
.value(if let Some(security) = &config.tauri.security {
|
|
|
|
security.csp.clone().unwrap_or_else(|| "unset".to_string())
|
|
|
|
} else {
|
|
|
|
"unset".to_string()
|
|
|
|
})
|
|
|
|
.display();
|
2021-05-31 19:33:44 +03:00
|
|
|
InfoBlock::new(" distDir")
|
2021-05-31 17:42:10 +03:00
|
|
|
.value(config.build.dist_dir.to_string())
|
2021-03-26 04:19:32 +03:00
|
|
|
.display();
|
2021-05-31 19:33:44 +03:00
|
|
|
InfoBlock::new(" devPath")
|
2021-05-31 17:42:10 +03:00
|
|
|
.value(config.build.dev_path.to_string())
|
2021-03-26 04:19:32 +03:00
|
|
|
.display();
|
|
|
|
}
|
2021-05-03 03:51:35 +03:00
|
|
|
if let Ok(package_json) = read_to_string(app_dir.join("package.json")) {
|
2021-06-01 01:37:53 +03:00
|
|
|
let (framework, bundler) = infer_framework(&package_json);
|
2021-05-03 03:51:35 +03:00
|
|
|
if let Some(framework) = framework {
|
2021-06-01 01:37:53 +03:00
|
|
|
InfoBlock::new(" framework")
|
|
|
|
.value(framework.to_string())
|
|
|
|
.display();
|
2021-05-03 03:51:35 +03:00
|
|
|
}
|
|
|
|
if let Some(bundler) = bundler {
|
2021-06-01 01:37:53 +03:00
|
|
|
InfoBlock::new(" bundler")
|
|
|
|
.value(bundler.to_string())
|
|
|
|
.display();
|
2021-05-03 03:51:35 +03:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
println!("package.json not found");
|
|
|
|
}
|
2021-03-26 04:19:32 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
2021-01-30 18:15:47 +03:00
|
|
|
}
|
|
|
|
}
|
2021-08-24 19:01:16 +03:00
|
|
|
|
|
|
|
fn get_package_manager<T: AsRef<str>>(file_names: &[T]) -> crate::Result<PackageManager> {
|
|
|
|
let mut use_npm = false;
|
|
|
|
let mut use_pnpm = false;
|
|
|
|
let mut use_yarn = false;
|
|
|
|
|
|
|
|
for name in file_names {
|
|
|
|
if name.as_ref() == "package-lock.json" {
|
|
|
|
use_npm = true;
|
|
|
|
} else if name.as_ref() == "pnpm-lock.yaml" {
|
|
|
|
use_pnpm = true;
|
|
|
|
} else if name.as_ref() == "yarn.lock" {
|
|
|
|
use_yarn = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !use_npm && !use_pnpm && !use_yarn {
|
|
|
|
println!("WARNING: no lock files found, defaulting to npm");
|
|
|
|
return Ok(PackageManager::Npm);
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut found = Vec::new();
|
|
|
|
|
|
|
|
if use_npm {
|
|
|
|
found.push("npm");
|
|
|
|
}
|
|
|
|
if use_pnpm {
|
|
|
|
found.push("pnpm");
|
|
|
|
}
|
|
|
|
if use_yarn {
|
|
|
|
found.push("yarn");
|
|
|
|
}
|
|
|
|
|
|
|
|
if found.len() > 1 {
|
|
|
|
return Err(anyhow::anyhow!(
|
|
|
|
"only one package mangager should be used, but found {}\nplease remove unused package manager lock files",
|
|
|
|
found.join(" and ")
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
if use_npm {
|
|
|
|
Ok(PackageManager::Npm)
|
|
|
|
} else if use_pnpm {
|
|
|
|
Ok(PackageManager::Pnpm)
|
|
|
|
} else {
|
|
|
|
Ok(PackageManager::Yarn)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use crate::info::get_package_manager;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn no_package_manager_lock_file() -> crate::Result<()> {
|
|
|
|
let file_names = vec!["package.json"];
|
|
|
|
let pm = get_package_manager(&file_names);
|
|
|
|
match pm {
|
|
|
|
Ok(_) => Ok(()),
|
|
|
|
Err(m) => Err(m),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn package_managers_npm_and_yarn() -> crate::Result<()> {
|
|
|
|
let file_names = vec!["package.json", "package-lock.json", "yarn.lock"];
|
|
|
|
let pm = get_package_manager(&file_names);
|
|
|
|
match pm {
|
|
|
|
Ok(_) => panic!("expected error"),
|
|
|
|
Err(m) => assert_eq!(
|
|
|
|
m.to_string().as_str(),
|
|
|
|
"only one package mangager should be used, but found npm and yarn\nplease remove unused package manager lock files"
|
|
|
|
),
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn package_managers_npm_and_pnpm() -> crate::Result<()> {
|
|
|
|
let file_names = vec!["package.json", "package-lock.json", "pnpm-lock.yaml"];
|
|
|
|
let pm = get_package_manager(&file_names);
|
|
|
|
match pm {
|
|
|
|
Ok(_) => panic!("expected error"),
|
|
|
|
Err(m) => assert_eq!(
|
|
|
|
m.to_string().as_str(),
|
|
|
|
"only one package mangager should be used, but found npm and pnpm\nplease remove unused package manager lock files"
|
|
|
|
),
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn package_managers_pnpm_and_yarn() -> crate::Result<()> {
|
|
|
|
let file_names = vec!["package.json", "pnpm-lock.yaml", "yarn.lock"];
|
|
|
|
let pm = get_package_manager(&file_names);
|
|
|
|
match pm {
|
|
|
|
Ok(_) => panic!("expected error"),
|
|
|
|
Err(m) => assert_eq!(
|
|
|
|
m.to_string().as_str(),
|
|
|
|
"only one package mangager should be used, but found pnpm and yarn\nplease remove unused package manager lock files"
|
|
|
|
),
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn package_managers_yarn() -> crate::Result<()> {
|
|
|
|
let file_names = vec!["package.json", "yarn.lock"];
|
|
|
|
let pm = get_package_manager(&file_names);
|
|
|
|
match pm {
|
|
|
|
Ok(_) => Ok(()),
|
|
|
|
Err(m) => Err(m),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|