1
0
Fork 0
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:
Bartek Iwańczuk 2022-10-19 23:30:44 +02:00 committed by GitHub
parent e3a3095481
commit 973069b341
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 180 additions and 22 deletions

View file

@ -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

View file

@ -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",

View 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"
}
}

View 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

View 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"
}
}

View 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

View 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"
}
}

View 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"
}
}

View 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"
}
}

View 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