From 5168700be669179382462214724115a3013cb58e Mon Sep 17 00:00:00 2001 From: Nathan Whitaker <17734409+nathanwhit@users.noreply.github.com> Date: Tue, 20 Aug 2024 07:55:47 -0700 Subject: [PATCH] feat(config): Support frozen lockfile config option in deno.json (#25100) Closes #24544 --- cli/args/flags.rs | 12 ++++---- cli/args/lockfile.rs | 18 ++++++++---- cli/lsp/config.rs | 14 +++++++-- cli/schemas/config-file.v1.json | 16 ++++++++-- cli/tools/installer.rs | 2 +- .../lockfile/frozen_lockfile/__test__.jsonc | 29 +++++++++++++++++++ 6 files changed, 73 insertions(+), 18 deletions(-) diff --git a/cli/args/flags.rs b/cli/args/flags.rs index 0ceac4563e..a273d8b370 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -589,7 +589,7 @@ pub struct Flags { pub argv: Vec, pub subcommand: DenoSubcommand, - pub frozen_lockfile: bool, + pub frozen_lockfile: Option, pub ca_stores: Option>, pub ca_data: Option, pub cache_blocklist: Vec, @@ -5231,7 +5231,7 @@ fn cached_only_arg_parse(flags: &mut Flags, matches: &mut ArgMatches) { fn frozen_lockfile_arg_parse(flags: &mut Flags, matches: &mut ArgMatches) { if let Some(&v) = matches.get_one::("frozen") { - flags.frozen_lockfile = v; + flags.frozen_lockfile = Some(v); } } @@ -10923,10 +10923,10 @@ mod tests { #[test] fn run_with_frozen_lockfile() { let cases = [ - (Some("--frozen"), true), - (Some("--frozen=true"), true), - (Some("--frozen=false"), false), - (None, false), + (Some("--frozen"), Some(true)), + (Some("--frozen=true"), Some(true)), + (Some("--frozen=false"), Some(false)), + (None, None), ]; for (flag, frozen) in cases { let mut args = svec!["deno", "run"]; diff --git a/cli/args/lockfile.rs b/cli/args/lockfile.rs index 953278e7dc..00d1f929d8 100644 --- a/cli/args/lockfile.rs +++ b/cli/args/lockfile.rs @@ -147,22 +147,28 @@ impl CliLockfile { }, }; + let root_folder = workspace.root_folder_configs(); + // CLI flag takes precedence over the config + let frozen = flags.frozen_lockfile.unwrap_or_else(|| { + root_folder + .deno_json + .as_ref() + .and_then(|c| c.to_lock_config().ok().flatten().map(|c| c.frozen())) + .unwrap_or(false) + }); + let lockfile = if flags.lock_write { log::warn!( "{} \"--lock-write\" flag is deprecated and will be removed in Deno 2.", crate::colors::yellow("Warning") ); - CliLockfile::new( - Lockfile::new_empty(filename, true), - flags.frozen_lockfile, - ) + CliLockfile::new(Lockfile::new_empty(filename, true), frozen) } else { - Self::read_from_path(filename, flags.frozen_lockfile)? + Self::read_from_path(filename, frozen)? }; // 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()), diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs index e32303baa9..f99f1fa103 100644 --- a/cli/lsp/config.rs +++ b/cli/lsp/config.rs @@ -1846,7 +1846,12 @@ fn resolve_lockfile_from_workspace( return None; } }; - resolve_lockfile_from_path(lockfile_path) + let frozen = workspace + .workspace + .root_deno_json() + .and_then(|c| c.to_lock_config().ok().flatten().map(|c| c.frozen())) + .unwrap_or(false); + resolve_lockfile_from_path(lockfile_path, frozen) } fn resolve_node_modules_dir( @@ -1875,8 +1880,11 @@ fn resolve_node_modules_dir( canonicalize_path_maybe_not_exists(&node_modules_dir).ok() } -fn resolve_lockfile_from_path(lockfile_path: PathBuf) -> Option { - match CliLockfile::read_from_path(lockfile_path, false) { +fn resolve_lockfile_from_path( + lockfile_path: PathBuf, + frozen: bool, +) -> Option { + match CliLockfile::read_from_path(lockfile_path, frozen) { Ok(value) => { if value.filename.exists() { if let Ok(specifier) = ModuleSpecifier::from_file_path(&value.filename) diff --git a/cli/schemas/config-file.v1.json b/cli/schemas/config-file.v1.json index adfe3b0c20..733a9cab60 100644 --- a/cli/schemas/config-file.v1.json +++ b/cli/schemas/config-file.v1.json @@ -552,8 +552,20 @@ }, "lock": { "description": "Whether to use a lock file or the path to use for the lock file. Can be overridden by CLI arguments.", - "type": ["string", "boolean"], - "default": true + "type": ["string", "boolean", "object"], + "default": true, + "properties": { + "path": { + "type": "string", + "description": "The path to use for the lock file.", + "default": "deno.lock" + }, + "frozen": { + "type": "boolean", + "description": "Whether to exit with an error if lock file is out of date.", + "default": false + } + } }, "unstable": { "type": "array", diff --git a/cli/tools/installer.rs b/cli/tools/installer.rs index 648b766b85..456e5c1a68 100644 --- a/cli/tools/installer.rs +++ b/cli/tools/installer.rs @@ -472,7 +472,7 @@ async fn resolve_shim_data( executable_args.push("--cached-only".to_string()); } - if flags.frozen_lockfile { + if flags.frozen_lockfile.unwrap_or(false) { executable_args.push("--frozen".to_string()); } diff --git a/tests/specs/lockfile/frozen_lockfile/__test__.jsonc b/tests/specs/lockfile/frozen_lockfile/__test__.jsonc index 8faa45b4e3..3036c8db52 100644 --- a/tests/specs/lockfile/frozen_lockfile/__test__.jsonc +++ b/tests/specs/lockfile/frozen_lockfile/__test__.jsonc @@ -123,6 +123,35 @@ } ] }, + + "lockfile_config": { + "steps": [ + { + "args": [ + "eval", + "Deno.writeTextFileSync('deno.json', JSON.stringify({ lock: { frozen: true }, ...JSON.parse(Deno.readTextFileSync('deno.json')) }))" + ], + "output": "" + }, + { + "args": "cache --frozen=false add.ts", + "output": "[WILDCARD]" + }, + { + // sub.ts imports from an npm package + // that's not in the lockfile + "args": "run sub.ts", + "output": "frozen_new_dep_run.out", + "exitCode": 1 + }, + { + "args": "cache sub.ts", + "output": "frozen_new_dep_cache.out", + "exitCode": 1 + } + ] + }, + "non_analyzable_dynamic_npm": { "steps": [ {