diff options
author | Bartek IwaĆczuk <biwanczuk@gmail.com> | 2022-11-01 00:07:36 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-11-01 00:07:36 +0100 |
commit | 89c5aa85984eabd300818f8275e3970f12a89754 (patch) | |
tree | fcebd710f4d4015ffdc022f1c78574de78ab29e9 /cli/lockfile.rs | |
parent | cb08b4683fc3a8feadc4a7590dc2db922ca3a6ed (diff) |
fix(lock): Additive lock file (#16500)
This commit changes lockfile to be "additive" - ie. integrity check only fails if
file/package is already specified in the lockfile, but its integrity doesn't match.
If file/package is not present in the lockfile, it will be added to the lockfile and
the lockfile will be written to disk.
Diffstat (limited to 'cli/lockfile.rs')
-rw-r--r-- | cli/lockfile.rs | 147 |
1 files changed, 122 insertions, 25 deletions
diff --git a/cli/lockfile.rs b/cli/lockfile.rs index 53a3c1286..78f5f2ab8 100644 --- a/cli/lockfile.rs +++ b/cli/lockfile.rs @@ -75,15 +75,16 @@ pub struct LockfileContent { #[derive(Debug, Clone)] pub struct Lockfile { - pub write: bool, + pub overwrite: bool, + pub has_content_changed: bool, pub content: LockfileContent, pub filename: PathBuf, } impl Lockfile { - pub fn new(filename: PathBuf, write: bool) -> Result<Lockfile, AnyError> { + pub fn new(filename: PathBuf, overwrite: bool) -> Result<Lockfile, AnyError> { // Writing a lock file always uses the new format. - let content = if write { + let content = if overwrite { LockfileContent { version: "2".to_string(), remote: BTreeMap::new(), @@ -114,7 +115,8 @@ impl Lockfile { }; Ok(Lockfile { - write, + overwrite, + has_content_changed: false, content, filename, }) @@ -122,7 +124,7 @@ impl Lockfile { // Synchronize lock file to disk - noop if --lock-write file is not specified. pub fn write(&self) -> Result<(), AnyError> { - if !self.write { + if !self.has_content_changed && !self.overwrite { return Ok(()); } @@ -148,12 +150,12 @@ impl Lockfile { specifier: &str, code: &str, ) -> bool { - if self.write { + if self.overwrite { // In case --lock-write is specified check always passes self.insert(specifier, code); true } else { - self.check(specifier, code) + self.check_or_insert(specifier, code) } } @@ -161,18 +163,18 @@ impl Lockfile { &mut self, package: &NpmResolutionPackage, ) -> Result<(), LockfileError> { - if self.write { + if self.overwrite { // In case --lock-write is specified check always passes - self.insert_npm_package(package); + self.insert_npm(package); Ok(()) } else { - self.check_npm_package(package) + self.check_or_insert_npm(package) } } - /// Checks the given module is included. - /// Returns Ok(true) if check passed. - fn check(&mut self, specifier: &str, code: &str) -> bool { + /// Checks the given module is included, if so verify the checksum. If module + /// is not included, insert it. + fn check_or_insert(&mut self, specifier: &str, code: &str) -> bool { if specifier.starts_with("file:") { return true; } @@ -180,7 +182,8 @@ impl Lockfile { let compiled_checksum = crate::checksum::gen(&[code.as_bytes()]); lockfile_checksum == &compiled_checksum } else { - false + self.insert(specifier, code); + true } } @@ -190,9 +193,10 @@ impl Lockfile { } let checksum = crate::checksum::gen(&[code.as_bytes()]); self.content.remote.insert(specifier.to_string(), checksum); + self.has_content_changed = true; } - fn check_npm_package( + fn check_or_insert_npm( &mut self, package: &NpmResolutionPackage, ) -> Result<(), LockfileError> { @@ -211,12 +215,14 @@ impl Lockfile { package.id, integrity, package_info.integrity ))); } + } else { + self.insert_npm(package); } Ok(()) } - fn insert_npm_package(&mut self, package: &NpmResolutionPackage) { + fn insert_npm(&mut self, package: &NpmResolutionPackage) { let dependencies = package .dependencies .iter() @@ -235,6 +241,7 @@ impl Lockfile { dependencies, }, ); + self.has_content_changed = true; } pub fn insert_npm_specifier( @@ -242,12 +249,11 @@ impl Lockfile { package_req: &NpmPackageReq, version: String, ) { - if self.write { - self.content.npm.specifiers.insert( - package_req.to_string(), - format!("{}@{}", package_req.name, version), - ); - } + self.content.npm.specifiers.insert( + package_req.to_string(), + format!("{}@{}", package_req.name, version), + ); + self.has_content_changed = true; } } @@ -290,8 +296,12 @@ pub fn as_maybe_locker( #[cfg(test)] mod tests { use super::*; + use crate::npm::NpmPackageId; + use crate::npm::NpmPackageVersionDistInfo; + use crate::npm::NpmVersion; use deno_core::serde_json; use deno_core::serde_json::json; + use std::collections::HashMap; use std::fs::File; use std::io::prelude::*; use std::io::Write; @@ -309,7 +319,16 @@ mod tests { }, "npm": { "specifiers": {}, - "packages": {} + "packages": { + "nanoid@3.3.4": { + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "dependencies": {} + }, + "picocolors@1.0.0": { + "integrity": "sha512-foobar", + "dependencies": {} + }, + } } }); @@ -424,7 +443,7 @@ mod tests { } #[test] - fn check_or_insert_lockfile_false() { + fn check_or_insert_lockfile() { let temp_dir = TempDir::new(); let file_path = setup(&temp_dir); @@ -443,8 +462,86 @@ mod tests { let check_false = lockfile.check_or_insert_remote( "https://deno.land/std@0.71.0/textproto/mod.ts", - "This is new Source code", + "Here is some NEW source code", ); assert!(!check_false); + + // Not present in lockfile yet, should be inserted and check passed. + let check_true = lockfile.check_or_insert_remote( + "https://deno.land/std@0.71.0/http/file_server.ts", + "This is new Source code", + ); + assert!(check_true); + } + + #[test] + fn check_or_insert_lockfile_npm() { + let temp_dir = TempDir::new(); + let file_path = setup(&temp_dir); + + let mut lockfile = Lockfile::new(file_path, false).unwrap(); + + let npm_package = NpmResolutionPackage { + id: NpmPackageId { + name: "nanoid".to_string(), + version: NpmVersion::parse("3.3.4").unwrap(), + }, + dist: NpmPackageVersionDistInfo { + tarball: "foo".to_string(), + shasum: "foo".to_string(), + integrity: Some("sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==".to_string()) + }, + dependencies: HashMap::new(), + }; + let check_ok = lockfile.check_or_insert_npm_package(&npm_package); + assert!(check_ok.is_ok()); + + let npm_package = NpmResolutionPackage { + id: NpmPackageId { + name: "picocolors".to_string(), + version: NpmVersion::parse("1.0.0").unwrap(), + }, + dist: NpmPackageVersionDistInfo { + tarball: "foo".to_string(), + shasum: "foo".to_string(), + integrity: Some("sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==".to_string()) + }, + dependencies: HashMap::new(), + }; + // Integrity is borked in the loaded lockfile + let check_err = lockfile.check_or_insert_npm_package(&npm_package); + assert!(check_err.is_err()); + + let npm_package = NpmResolutionPackage { + id: NpmPackageId { + name: "source-map-js".to_string(), + version: NpmVersion::parse("1.0.2").unwrap(), + }, + dist: NpmPackageVersionDistInfo { + tarball: "foo".to_string(), + shasum: "foo".to_string(), + integrity: Some("sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==".to_string()) + }, + dependencies: HashMap::new(), + }; + // Not present in lockfile yet, should be inserted and check passed. + let check_ok = lockfile.check_or_insert_npm_package(&npm_package); + assert!(check_ok.is_ok()); + + let npm_package = NpmResolutionPackage { + id: NpmPackageId { + name: "source-map-js".to_string(), + version: NpmVersion::parse("1.0.2").unwrap(), + }, + dist: NpmPackageVersionDistInfo { + tarball: "foo".to_string(), + shasum: "foo".to_string(), + integrity: Some("sha512-foobar".to_string()), + }, + dependencies: HashMap::new(), + }; + // Now present in lockfile, should file due to borked integrity + let check_err = lockfile.check_or_insert_npm_package(&npm_package); + assert!(check_err.is_err()); } } |