mirror of
https://github.com/denoland/deno.git
synced 2024-12-22 07:14:47 -05:00
feat: Add new lockfile format (#16349)
Introduces a new lockfile format that will be used to support locking "npm" dependencies. Currently the format looks as follows: ``` // This file is automatically generated by Deno, do not edit its contents // manually. This file should be commited to your repository. { "version": "2", "remote": { "https://deno.land/std@0.160.0/http/server.ts": "asdwetsw44523asdfgfas..", "https://deno.land/std@0.160.0/http/file_server.ts": "asdwetsw44523asdfgfas.." } } ``` A follow up PR will add "npm" key that will be used to store information related to "npm" dependencies and their resolution. The new format is used when `--lock-write` is present, if user tries to load a lock file using the old format it will still work.
This commit is contained in:
parent
e3a3095481
commit
973069b341
10 changed files with 180 additions and 22 deletions
109
cli/lockfile.rs
109
cli/lockfile.rs
|
@ -1,8 +1,12 @@
|
|||
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
use deno_core::anyhow::Context;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
use deno_core::serde::Deserialize;
|
||||
use deno_core::serde::Serialize;
|
||||
use deno_core::serde_json;
|
||||
use deno_core::serde_json::json;
|
||||
use deno_core::ModuleSpecifier;
|
||||
|
@ -16,9 +20,20 @@ use std::sync::Arc;
|
|||
|
||||
use crate::tools::fmt::format_json;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct LockfileV2Content {
|
||||
version: String,
|
||||
// Mapping between URLs and their checksums
|
||||
remote: BTreeMap<String, String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct LockfileV1Content(BTreeMap<String, String>);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LockfileContent {
|
||||
map: BTreeMap<String, String>,
|
||||
enum LockfileContent {
|
||||
V1(LockfileV1Content),
|
||||
V2(LockfileV2Content),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -30,19 +45,33 @@ pub struct Lockfile {
|
|||
|
||||
impl Lockfile {
|
||||
pub fn new(filename: PathBuf, write: bool) -> Result<Lockfile, AnyError> {
|
||||
let map = if write {
|
||||
BTreeMap::new()
|
||||
// Writing a lock file always uses the new format.
|
||||
let content = if write {
|
||||
LockfileContent::V2(LockfileV2Content {
|
||||
version: "2".to_string(),
|
||||
remote: BTreeMap::new(),
|
||||
})
|
||||
} else {
|
||||
let s = std::fs::read_to_string(&filename).with_context(|| {
|
||||
format!("Unable to read lockfile: {}", filename.display())
|
||||
})?;
|
||||
serde_json::from_str(&s)
|
||||
.context("Unable to parse contents of the lockfile")?
|
||||
let value: serde_json::Value = serde_json::from_str(&s)
|
||||
.context("Unable to parse contents of the lockfile")?;
|
||||
let version = value.get("version").and_then(|v| v.as_str());
|
||||
if version == Some("2") {
|
||||
let content: LockfileV2Content =
|
||||
serde_json::from_value(value).context("Unable to parse lockfile")?;
|
||||
LockfileContent::V2(content)
|
||||
} else {
|
||||
let content: LockfileV1Content =
|
||||
serde_json::from_value(value).context("Unable to parse lockfile")?;
|
||||
LockfileContent::V1(content)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Lockfile {
|
||||
write,
|
||||
content: LockfileContent { map },
|
||||
content,
|
||||
filename,
|
||||
})
|
||||
}
|
||||
|
@ -52,13 +81,19 @@ impl Lockfile {
|
|||
if !self.write {
|
||||
return Ok(());
|
||||
}
|
||||
let j = json!(&self.content.map);
|
||||
let s = serde_json::to_string_pretty(&j).unwrap();
|
||||
|
||||
let format_s = format_json(&s, &Default::default())
|
||||
let json_string = match &self.content {
|
||||
LockfileContent::V1(c) => {
|
||||
let j = json!(&c.0);
|
||||
serde_json::to_string(&j).unwrap()
|
||||
}
|
||||
LockfileContent::V2(c) => serde_json::to_string(&c).unwrap(),
|
||||
};
|
||||
|
||||
let format_s = format_json(&json_string, &Default::default())
|
||||
.ok()
|
||||
.flatten()
|
||||
.unwrap_or(s);
|
||||
.unwrap_or(json_string);
|
||||
let mut f = std::fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
|
@ -85,11 +120,23 @@ impl Lockfile {
|
|||
if specifier.starts_with("file:") {
|
||||
return true;
|
||||
}
|
||||
if let Some(lockfile_checksum) = self.content.map.get(specifier) {
|
||||
let compiled_checksum = crate::checksum::gen(&[code.as_bytes()]);
|
||||
lockfile_checksum == &compiled_checksum
|
||||
} else {
|
||||
false
|
||||
match &self.content {
|
||||
LockfileContent::V1(c) => {
|
||||
if let Some(lockfile_checksum) = c.0.get(specifier) {
|
||||
let compiled_checksum = crate::checksum::gen(&[code.as_bytes()]);
|
||||
lockfile_checksum == &compiled_checksum
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
LockfileContent::V2(c) => {
|
||||
if let Some(lockfile_checksum) = c.remote.get(specifier) {
|
||||
let compiled_checksum = crate::checksum::gen(&[code.as_bytes()]);
|
||||
lockfile_checksum == &compiled_checksum
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,7 +145,14 @@ impl Lockfile {
|
|||
return;
|
||||
}
|
||||
let checksum = crate::checksum::gen(&[code.as_bytes()]);
|
||||
self.content.map.insert(specifier.to_string(), checksum);
|
||||
match &mut self.content {
|
||||
LockfileContent::V1(c) => {
|
||||
c.0.insert(specifier.to_string(), checksum);
|
||||
}
|
||||
LockfileContent::V2(c) => {
|
||||
c.remote.insert(specifier.to_string(), checksum);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -154,8 +208,11 @@ mod tests {
|
|||
let mut file = File::create(file_path).expect("write file fail");
|
||||
|
||||
let value: serde_json::Value = json!({
|
||||
"https://deno.land/std@0.71.0/textproto/mod.ts": "3118d7a42c03c242c5a49c2ad91c8396110e14acca1324e7aaefd31a999b71a4",
|
||||
"https://deno.land/std@0.71.0/async/delay.ts": "35957d585a6e3dd87706858fb1d6b551cb278271b03f52c5a2cb70e65e00c26a"
|
||||
"version": "2",
|
||||
"remote": {
|
||||
"https://deno.land/std@0.71.0/textproto/mod.ts": "3118d7a42c03c242c5a49c2ad91c8396110e14acca1324e7aaefd31a999b71a4",
|
||||
"https://deno.land/std@0.71.0/async/delay.ts": "35957d585a6e3dd87706858fb1d6b551cb278271b03f52c5a2cb70e65e00c26a"
|
||||
}
|
||||
});
|
||||
|
||||
file.write_all(value.to_string().as_bytes()).unwrap();
|
||||
|
@ -176,7 +233,11 @@ mod tests {
|
|||
|
||||
let result = Lockfile::new(file_path, false).unwrap();
|
||||
|
||||
let keys: Vec<String> = result.content.map.keys().cloned().collect();
|
||||
let remote = match result.content {
|
||||
LockfileContent::V2(c) => c.remote,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let keys: Vec<String> = remote.keys().cloned().collect();
|
||||
let expected_keys = vec![
|
||||
String::from("https://deno.land/std@0.71.0/async/delay.ts"),
|
||||
String::from("https://deno.land/std@0.71.0/textproto/mod.ts"),
|
||||
|
@ -198,7 +259,11 @@ mod tests {
|
|||
"Here is some source code",
|
||||
);
|
||||
|
||||
let keys: Vec<String> = lockfile.content.map.keys().cloned().collect();
|
||||
let remote = match lockfile.content {
|
||||
LockfileContent::V2(c) => c.remote,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let keys: Vec<String> = remote.keys().cloned().collect();
|
||||
let expected_keys = vec![
|
||||
String::from("https://deno.land/std@0.71.0/async/delay.ts"),
|
||||
String::from("https://deno.land/std@0.71.0/io/util.ts"),
|
||||
|
@ -242,7 +307,7 @@ mod tests {
|
|||
|
||||
let contents_json =
|
||||
serde_json::from_str::<serde_json::Value>(&contents).unwrap();
|
||||
let object = contents_json.as_object().unwrap();
|
||||
let object = contents_json["remote"].as_object().unwrap();
|
||||
|
||||
assert_eq!(
|
||||
object
|
||||
|
|
|
@ -658,6 +658,40 @@ itest!(lock_check_err2 {
|
|||
http_server: true,
|
||||
});
|
||||
|
||||
itest!(lock_v2_check_ok {
|
||||
args:
|
||||
"run --lock=run/lock_v2_check_ok.json http://127.0.0.1:4545/run/003_relative_import.ts",
|
||||
output: "run/003_relative_import.ts.out",
|
||||
http_server: true,
|
||||
});
|
||||
|
||||
itest!(lock_v2_check_ok2 {
|
||||
args: "run --lock=run/lock_v2_check_ok2.json run/019_media_types.ts",
|
||||
output: "run/019_media_types.ts.out",
|
||||
http_server: true,
|
||||
});
|
||||
|
||||
itest!(lock_v2_dynamic_imports {
|
||||
args: "run --lock=run/lock_v2_dynamic_imports.json --allow-read --allow-net http://127.0.0.1:4545/run/013_dynamic_import.ts",
|
||||
output: "run/lock_v2_dynamic_imports.out",
|
||||
exit_code: 10,
|
||||
http_server: true,
|
||||
});
|
||||
|
||||
itest!(lock_v2_check_err {
|
||||
args: "run --lock=run/lock_v2_check_err.json http://127.0.0.1:4545/run/003_relative_import.ts",
|
||||
output: "run/lock_v2_check_err.out",
|
||||
exit_code: 10,
|
||||
http_server: true,
|
||||
});
|
||||
|
||||
itest!(lock_v2_check_err2 {
|
||||
args: "run --lock=run/lock_v2_check_err2.json run/019_media_types.ts",
|
||||
output: "run/lock_v2_check_err2.out",
|
||||
exit_code: 10,
|
||||
http_server: true,
|
||||
});
|
||||
|
||||
itest!(mts_dmts_mjs {
|
||||
args: "run subdir/import.mts",
|
||||
output: "run/mts_dmts_mjs.out",
|
||||
|
|
7
cli/tests/testdata/run/lock_v2_check_err.json
vendored
Normal file
7
cli/tests/testdata/run/lock_v2_check_err.json
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"version": "2",
|
||||
"remote": {
|
||||
"http://127.0.0.1:4545/subdir/print_hello.ts": "fa6692c8f9ff3fb107e773c3ece5274e9d08be282867a1e3ded1d9c00fcaa63c",
|
||||
"http://127.0.0.1:4545/run/003_relative_import.ts": "bad"
|
||||
}
|
||||
}
|
3
cli/tests/testdata/run/lock_v2_check_err.out
vendored
Normal file
3
cli/tests/testdata/run/lock_v2_check_err.out
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
[WILDCARD]The source code is invalid, as it does not match the expected hash in the lock file.
|
||||
Specifier: http://127.0.0.1:4545/run/003_relative_import.ts
|
||||
Lock file: run/lock_v2_check_err.json
|
13
cli/tests/testdata/run/lock_v2_check_err2.json
vendored
Normal file
13
cli/tests/testdata/run/lock_v2_check_err2.json
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"version": "2",
|
||||
"remote": {
|
||||
"http://localhost:4545/subdir/mt_application_ecmascript.j2.js": "3a3e002e2f92dc8f045bd4a7c66b4791453ad0417b038dd2b2d9d0f277c44f18",
|
||||
"http://localhost:4545/subdir/mt_application_x_javascript.j4.js": "3a3e002e2f92dc8f045bd4a7c66b4791453ad0417b038dd2b2d9d0f277c44f18",
|
||||
"http://localhost:4545/subdir/mt_application_x_typescript.t4.ts": "3a3e002e2f92dc8f045bd4a7c66b4791453ad0417b038dd2b2d9d0f277c44f18",
|
||||
"http://localhost:4545/subdir/mt_text_ecmascript.j3.js": "bad",
|
||||
"http://localhost:4545/subdir/mt_text_javascript.j1.js": "3a3e002e2f92dc8f045bd4a7c66b4791453ad0417b038dd2b2d9d0f277c44f18",
|
||||
"http://localhost:4545/subdir/mt_text_typescript.t1.ts": "3a3e002e2f92dc8f045bd4a7c66b4791453ad0417b038dd2b2d9d0f277c44f18",
|
||||
"http://localhost:4545/subdir/mt_video_mp2t.t3.ts": "3a3e002e2f92dc8f045bd4a7c66b4791453ad0417b038dd2b2d9d0f277c44f18",
|
||||
"http://localhost:4545/subdir/mt_video_vdn.t2.ts": "3a3e002e2f92dc8f045bd4a7c66b4791453ad0417b038dd2b2d9d0f277c44f18"
|
||||
}
|
||||
}
|
3
cli/tests/testdata/run/lock_v2_check_err2.out
vendored
Normal file
3
cli/tests/testdata/run/lock_v2_check_err2.out
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
[WILDCARD]The source code is invalid, as it does not match the expected hash in the lock file.
|
||||
Specifier: http://localhost:4545/subdir/mt_text_ecmascript.j3.js
|
||||
Lock file: run/lock_v2_check_err2.json
|
7
cli/tests/testdata/run/lock_v2_check_ok.json
vendored
Normal file
7
cli/tests/testdata/run/lock_v2_check_ok.json
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"version": "2",
|
||||
"remote": {
|
||||
"http://127.0.0.1:4545/subdir/print_hello.ts": "fa6692c8f9ff3fb107e773c3ece5274e9d08be282867a1e3ded1d9c00fcaa63c",
|
||||
"http://127.0.0.1:4545/run/003_relative_import.ts": "a1572e8fd2c2712b33f04aed2561505b5feb2c8696f1f2cded3de7127931b97e"
|
||||
}
|
||||
}
|
13
cli/tests/testdata/run/lock_v2_check_ok2.json
vendored
Normal file
13
cli/tests/testdata/run/lock_v2_check_ok2.json
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"version": "2",
|
||||
"remote": {
|
||||
"http://localhost:4545/subdir/mt_application_ecmascript.j2.js": "3a3e002e2f92dc8f045bd4a7c66b4791453ad0417b038dd2b2d9d0f277c44f18",
|
||||
"http://localhost:4545/subdir/mt_application_x_javascript.j4.js": "3a3e002e2f92dc8f045bd4a7c66b4791453ad0417b038dd2b2d9d0f277c44f18",
|
||||
"http://localhost:4545/subdir/mt_application_x_typescript.t4.ts": "3a3e002e2f92dc8f045bd4a7c66b4791453ad0417b038dd2b2d9d0f277c44f18",
|
||||
"http://localhost:4545/subdir/mt_text_ecmascript.j3.js": "3a3e002e2f92dc8f045bd4a7c66b4791453ad0417b038dd2b2d9d0f277c44f18",
|
||||
"http://localhost:4545/subdir/mt_text_javascript.j1.js": "3a3e002e2f92dc8f045bd4a7c66b4791453ad0417b038dd2b2d9d0f277c44f18",
|
||||
"http://localhost:4545/subdir/mt_text_typescript.t1.ts": "3a3e002e2f92dc8f045bd4a7c66b4791453ad0417b038dd2b2d9d0f277c44f18",
|
||||
"http://localhost:4545/subdir/mt_video_mp2t.t3.ts": "3a3e002e2f92dc8f045bd4a7c66b4791453ad0417b038dd2b2d9d0f277c44f18",
|
||||
"http://localhost:4545/subdir/mt_video_vdn.t2.ts": "3a3e002e2f92dc8f045bd4a7c66b4791453ad0417b038dd2b2d9d0f277c44f18"
|
||||
}
|
||||
}
|
9
cli/tests/testdata/run/lock_v2_dynamic_imports.json
vendored
Normal file
9
cli/tests/testdata/run/lock_v2_dynamic_imports.json
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"version": "2",
|
||||
"remote": {
|
||||
"http://127.0.0.1:4545/run/013_dynamic_import.ts": "3f83e653329dc1f963761a986997d710b9763f667fc243eef89b3a5decacda30",
|
||||
"http://127.0.0.1:4545/subdir/mod1.ts": "bfc1037b02c99abc20367f739bca7455813a5950066abd77965bff33b6eece0f",
|
||||
"http://127.0.0.1:4545/subdir/print_hello.ts": "fa6692c8f9ff3fb107e773c3ece5274e9d08be282867a1e3ded1d9c00fcaa63c",
|
||||
"http://127.0.0.1:4545/subdir/subdir2/mod2.ts": "bad"
|
||||
}
|
||||
}
|
4
cli/tests/testdata/run/lock_v2_dynamic_imports.out
vendored
Normal file
4
cli/tests/testdata/run/lock_v2_dynamic_imports.out
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
[WILDCARD]
|
||||
error: The source code is invalid, as it does not match the expected hash in the lock file.
|
||||
Specifier: http://127.0.0.1:4545/subdir/subdir2/mod2.ts
|
||||
Lock file: run/lock_v2_dynamic_imports.json
|
Loading…
Reference in a new issue