From 9114a2df69da9318c4e10887553b7daf77b0fa16 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Tue, 23 Jul 2024 19:00:48 -0400 Subject: fix(upgrade): do not error if config in cwd invalid (#24689) ``` > deno upgrade error: Unsupported lockfile version 'invalid'. Try upgrading Deno or recreating the lockfile. V:\scratch > V:\deno\target\debug\deno upgrade Looking up latest version Local deno version 1.45.3 is the most recent release ``` Closes #24517 Closes #20729 --- cli/args/lockfile.rs | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++ cli/args/mod.rs | 73 ++++++++++------------------------------------ 2 files changed, 97 insertions(+), 58 deletions(-) (limited to 'cli/args') diff --git a/cli/args/lockfile.rs b/cli/args/lockfile.rs index fa505e7b1..c9afecd98 100644 --- a/cli/args/lockfile.rs +++ b/cli/args/lockfile.rs @@ -1,12 +1,17 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::collections::BTreeSet; use std::path::PathBuf; +use deno_config::deno_json::ConfigFile; use deno_config::workspace::Workspace; use deno_core::anyhow::Context; use deno_core::error::AnyError; use deno_core::parking_lot::Mutex; use deno_core::parking_lot::MutexGuard; +use deno_lockfile::WorkspaceMemberConfig; +use deno_package_json::PackageJsonDepValue; +use deno_runtime::deno_node::PackageJson; use crate::cache; use crate::util::fs::atomic_write_file_with_retries; @@ -93,6 +98,35 @@ impl CliLockfile { flags: &Flags, workspace: &Workspace, ) -> Result, AnyError> { + fn pkg_json_deps(maybe_pkg_json: Option<&PackageJson>) -> BTreeSet { + let Some(pkg_json) = maybe_pkg_json else { + return Default::default(); + }; + pkg_json + .resolve_local_package_json_deps() + .values() + .filter_map(|dep| dep.as_ref().ok()) + .filter_map(|dep| match dep { + PackageJsonDepValue::Req(req) => Some(req), + PackageJsonDepValue::Workspace(_) => None, + }) + .map(|r| format!("npm:{}", r)) + .collect() + } + + fn deno_json_deps( + maybe_deno_json: Option<&ConfigFile>, + ) -> BTreeSet { + maybe_deno_json + .map(|c| { + crate::args::deno_json::deno_json_deps(c) + .into_iter() + .map(|req| req.to_string()) + .collect() + }) + .unwrap_or_default() + } + if flags.no_lock || matches!( flags.subcommand, @@ -125,6 +159,54 @@ impl CliLockfile { } else { Self::read_from_path(filename, flags.frozen_lockfile)? }; + + // initialize the lockfile with the workspace's configuration + let root_url = workspace.root_dir(); + let root_folder = workspace.root_folder_configs(); + let config = deno_lockfile::WorkspaceConfig { + root: WorkspaceMemberConfig { + package_json_deps: pkg_json_deps(root_folder.pkg_json.as_deref()), + dependencies: deno_json_deps(root_folder.deno_json.as_deref()), + }, + members: workspace + .config_folders() + .iter() + .filter(|(folder_url, _)| *folder_url != root_url) + .filter_map(|(folder_url, folder)| { + Some(( + { + // should never be None here, but just ignore members that + // do fail for this + let mut relative_path = root_url.make_relative(folder_url)?; + if relative_path.ends_with('/') { + // make it slightly cleaner by removing the trailing slash + relative_path.pop(); + } + relative_path + }, + { + let config = WorkspaceMemberConfig { + package_json_deps: pkg_json_deps(folder.pkg_json.as_deref()), + dependencies: deno_json_deps(folder.deno_json.as_deref()), + }; + if config.package_json_deps.is_empty() + && config.dependencies.is_empty() + { + // exclude empty workspace members + return None; + } + config + }, + )) + }) + .collect(), + }; + lockfile.set_workspace_config(deno_lockfile::SetWorkspaceConfigOptions { + no_npm: flags.no_npm, + no_config: flags.config_flag == super::ConfigFlag::Disabled, + config, + }); + Ok(Some(lockfile)) } pub fn read_from_path( diff --git a/cli/args/mod.rs b/cli/args/mod.rs index ba2e06e06..aea6ed8a8 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -26,7 +26,6 @@ use deno_npm::npm_rc::ResolvedNpmRc; use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot; use deno_npm::NpmSystemInfo; use deno_runtime::deno_permissions::PermissionsContainer; -use deno_runtime::deno_tls::RootCertStoreProvider; use deno_semver::npm::NpmPackageReqReference; use import_map::resolve_import_map_value_from_specifier; @@ -62,7 +61,6 @@ use deno_runtime::inspector_server::InspectorServer; use deno_terminal::colors; use dotenvy::from_filename; use once_cell::sync::Lazy; -use once_cell::sync::OnceCell; use serde::Deserialize; use serde::Serialize; use std::collections::HashMap; @@ -599,43 +597,6 @@ pub fn create_default_npmrc() -> Arc { }) } -struct CliRootCertStoreProvider { - cell: OnceCell, - maybe_root_path: Option, - maybe_ca_stores: Option>, - maybe_ca_data: Option, -} - -impl CliRootCertStoreProvider { - pub fn new( - maybe_root_path: Option, - maybe_ca_stores: Option>, - maybe_ca_data: Option, - ) -> Self { - Self { - cell: Default::default(), - maybe_root_path, - maybe_ca_stores, - maybe_ca_data, - } - } -} - -impl RootCertStoreProvider for CliRootCertStoreProvider { - fn get_or_try_init(&self) -> Result<&RootCertStore, AnyError> { - self - .cell - .get_or_try_init(|| { - get_root_cert_store( - self.maybe_root_path.clone(), - self.maybe_ca_stores.clone(), - self.maybe_ca_data.clone(), - ) - }) - .map_err(|e| e.into()) - } -} - #[derive(Error, Debug, Clone)] pub enum RootCertStoreLoadError { #[error( @@ -761,7 +722,7 @@ struct CliOptionOverrides { pub struct CliOptions { // the source of the options is a detail the rest of the // application need not concern itself with, so keep these private - flags: Flags, + flags: Arc, initial_cwd: PathBuf, maybe_node_modules_folder: Option, npmrc: Arc, @@ -774,7 +735,7 @@ pub struct CliOptions { impl CliOptions { pub fn new( - flags: Flags, + flags: Arc, initial_cwd: PathBuf, maybe_lockfile: Option>, npmrc: Arc, @@ -830,7 +791,7 @@ impl CliOptions { }) } - pub fn from_flags(flags: Flags) -> Result { + pub fn from_flags(flags: Arc) -> Result { let initial_cwd = std::env::current_dir().with_context(|| "Failed getting cwd.")?; let maybe_vendor_override = flags.vendor.map(|v| match v { @@ -920,6 +881,16 @@ impl CliOptions { ) } + /// This method is purposefully verbose to disourage its use. Do not use it + /// except in the factory structs. Instead, prefer specific methods on `CliOptions` + /// that can take all sources of information into account (ex. config files or env vars). + pub fn into_self_and_flags( + self: Arc, + ) -> (Arc, Arc) { + let flags = self.flags.clone(); + (self, flags) + } + #[inline(always)] pub fn initial_cwd(&self) -> &Path { &self.initial_cwd @@ -1241,16 +1212,6 @@ impl CliOptions { self.workspace().vendor_dir_path() } - pub fn resolve_root_cert_store_provider( - &self, - ) -> Arc { - Arc::new(CliRootCertStoreProvider::new( - None, - self.flags.ca_stores.clone(), - self.flags.ca_data.clone(), - )) - } - pub fn resolve_ts_config_for_emit( &self, config_type: TsConfigType, @@ -1290,8 +1251,8 @@ impl CliOptions { Ok(Some(InspectorServer::new(host, version::get_user_agent())?)) } - pub fn maybe_lockfile(&self) -> Option> { - self.maybe_lockfile.clone() + pub fn maybe_lockfile(&self) -> Option<&Arc> { + self.maybe_lockfile.as_ref() } pub fn to_compiler_option_types( @@ -1537,10 +1498,6 @@ impl CliOptions { self.flags.no_npm } - pub fn no_config(&self) -> bool { - self.flags.config_flag == ConfigFlag::Disabled - } - pub fn permission_flags(&self) -> &PermissionFlags { &self.flags.permissions } -- cgit v1.2.3