From 804fd318e783a093a7587d182222ce9055b799f7 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Mon, 27 May 2024 18:15:49 -0400 Subject: [PATCH] perf: move jsr manifest checksum to locker (#488) --- src/graph.rs | 40 ++++++---- src/jsr.rs | 30 +++---- src/packages.rs | 67 ++++------------ src/source/mod.rs | 60 ++++++++++---- tests/helpers/mod.rs | 27 +++++-- tests/specs/graph/checksums/invalid.txt | 4 +- tests/specs/graph/checksums/jsr_pkg_fills.txt | 76 ++++++++++++++++++ .../specs/graph/checksums/jsr_pkg_invalid.txt | 70 +++++++++++++++++ tests/specs/graph/checksums/jsr_pkg_valid.txt | 78 +++++++++++++++++++ .../checksums/redirect_with_checksum.txt | 4 +- .../graph/checksums/redirected_to_invalid.txt | 4 +- tests/specs/graph/checksums/valid.txt | 4 +- tests/specs_test.rs | 55 +++++++++---- 13 files changed, 395 insertions(+), 124 deletions(-) create mode 100644 tests/specs/graph/checksums/jsr_pkg_fills.txt create mode 100644 tests/specs/graph/checksums/jsr_pkg_invalid.txt create mode 100644 tests/specs/graph/checksums/jsr_pkg_valid.txt diff --git a/src/graph.rs b/src/graph.rs index ff0ae8944..8e78dc8c3 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -174,7 +174,7 @@ pub enum JsrLoadError { ContentLoadExternalSpecifier, #[error(transparent)] ContentLoad(Arc), - #[error("JSR package manifest for '{}' failed to load: {:#}", .0, .1)] + #[error("JSR package manifest for '{}' failed to load. {:#}", .0, .1)] PackageManifestLoad(String, Arc), #[error("JSR package not found: {}", .0)] PackageNotFound(String), @@ -3057,7 +3057,7 @@ struct PendingModuleLoadItem { struct LoadedJsrPackageViaHttpsUrl { nv: PackageNv, - manifest_checksum: String, + manifest_checksum_for_locker: Option, } type PendingInfoFuture<'a> = LocalBoxFuture<'a, PendingInfo>; @@ -3282,10 +3282,12 @@ impl<'a, 'graph> Builder<'a, 'graph> { loaded_package_via_https_url, }) => { if let Some(pkg) = loaded_package_via_https_url { - self - .graph - .packages - .ensure_package(pkg.nv, pkg.manifest_checksum); + if let Some(locker) = &mut self.locker { + if let Some(checksum) = pkg.manifest_checksum_for_locker { + locker.set_pkg_manifest_checksum(&pkg.nv, checksum); + } + } + self.graph.packages.ensure_package(pkg.nv); } match result { @@ -3459,10 +3461,12 @@ impl<'a, 'graph> Builder<'a, 'graph> { match version_info_result { Ok(version_info_load_item) => { let version_info = version_info_load_item.info; - self - .graph - .packages - .ensure_package(nv.clone(), version_info_load_item.checksum); + self.graph.packages.ensure_package(nv.clone()); + if let Some(locker) = &mut self.locker { + if let Some(checksum) = version_info_load_item.checksum_for_locker { + locker.set_pkg_manifest_checksum(nv, checksum); + } + } let base_url = self.jsr_url_provider.package_url(nv); let export_name = normalize_export_name(resolution_item.nv_ref.sub_path()); @@ -4277,7 +4281,7 @@ impl<'a, 'graph> Builder<'a, 'graph> { maybe_checksum = self .locker .as_ref() - .and_then(|l| l.get_checksum(&requested_specifier)); + .and_then(|l| l.get_remote_checksum(&requested_specifier)); } let fut = async move { #[allow(clippy::too_many_arguments)] @@ -4328,7 +4332,7 @@ impl<'a, 'graph> Builder<'a, 'graph> { maybe_version_info.replace(info); loaded_package_via_https_url.replace(LoadedJsrPackageViaHttpsUrl { nv: package_nv, - manifest_checksum: inner.checksum, + manifest_checksum_for_locker: inner.checksum_for_locker, }); } @@ -4417,7 +4421,11 @@ impl<'a, 'graph> Builder<'a, 'graph> { Ok(err) => Err(ModuleError::LoadingErr( load_specifier.clone(), maybe_range.cloned(), - ModuleLoadError::HttpsChecksumIntegrity(err), + if maybe_version_info.is_some() { + JsrLoadError::ContentChecksumIntegrity(err).into() + } else { + ModuleLoadError::HttpsChecksumIntegrity(err) + }, )), Err(err) => Err(ModuleError::LoadingErr( load_specifier.clone(), @@ -4474,7 +4482,7 @@ impl<'a, 'graph> Builder<'a, 'graph> { self.state.jsr.metadata.queue_load_package_version_info( package_nv, self.fill_pass_mode.to_cache_setting(), - self.graph.packages.get_manifest_checksum(package_nv), + self.locker.as_deref(), JsrMetadataStoreServices { executor: self.executor, jsr_url_provider: self.jsr_url_provider, @@ -4551,8 +4559,8 @@ impl<'a, 'graph> Builder<'a, 'graph> { && matches!(specifier.scheme(), "https" | "http") { if let Some(locker) = &mut self.locker { - if !locker.has_checksum(&specifier) { - locker.set_checksum( + if !locker.has_remote_checksum(&specifier) { + locker.set_remote_checksum( &specifier, LoaderChecksum::new(LoaderChecksum::gen( module_source_and_info.source().as_bytes(), diff --git a/src/jsr.rs b/src/jsr.rs index 50b3d7fe8..fce73585d 100644 --- a/src/jsr.rs +++ b/src/jsr.rs @@ -20,11 +20,12 @@ use crate::source::LoadOptions; use crate::source::LoadResponse; use crate::source::Loader; use crate::source::LoaderChecksum; +use crate::source::Locker; use crate::Executor; #[derive(Clone)] pub struct PendingJsrPackageVersionInfoLoadItem { - pub checksum: String, + pub checksum_for_locker: Option, pub info: Arc, } @@ -103,7 +104,7 @@ impl JsrMetadataStore { &mut self, package_nv: &PackageNv, cache_setting: CacheSetting, - maybe_expected_checksum: Option<&str>, + maybe_locker: Option<&dyn Locker>, services: JsrMetadataStoreServices, ) { if self @@ -121,24 +122,27 @@ impl JsrMetadataStore { package_nv.name, package_nv.version )) .unwrap(); - let maybe_expected_checksum = - maybe_expected_checksum.map(|c| LoaderChecksum::new(c.to_string())); + let maybe_expected_checksum = maybe_locker + .as_ref() + .and_then(|locker| locker.get_pkg_manifest_checksum(package_nv)); + let should_compute_checksum = + maybe_expected_checksum.is_none() && maybe_locker.is_some(); let fut = self.load_data( specifier, services, cache_setting, - // we won't have a checksum when loading this the - // first time or when not using a lockfile - maybe_expected_checksum.clone(), - |content| { - // if we have the expected checksum, then we can re-use that here - let checksum = maybe_expected_checksum - .map(|c| c.into_string()) - .unwrap_or_else(|| LoaderChecksum::gen(content)); + // we won't have a checksum when not using a lockfile + maybe_expected_checksum, + move |content| { + let checksum_for_locker = if should_compute_checksum { + Some(LoaderChecksum::new(LoaderChecksum::gen(content))) + } else { + None + }; let version_info: JsrPackageVersionInfo = serde_json::from_slice(content)?; Ok(PendingJsrPackageVersionInfoLoadItem { - checksum, + checksum_for_locker, info: Arc::new(version_info), }) }, diff --git a/src/packages.rs b/src/packages.rs index 3da672287..da8baeadd 100644 --- a/src/packages.rs +++ b/src/packages.rs @@ -105,19 +105,11 @@ impl JsrPackageVersionInfo { #[derive(Debug, Clone)] struct PackageNvInfo { - manifest_checksum: String, /// Collection of exports used. exports: BTreeMap, found_dependencies: HashSet, } -#[derive(Debug, Clone)] -pub struct PackageManifestIntegrityError { - pub nv: PackageNv, - pub actual: String, - pub expected: String, -} - #[derive(Debug, Clone, Default, Serialize)] pub struct PackageSpecifiers { #[serde(flatten)] @@ -138,10 +130,20 @@ impl PackageSpecifiers { self.package_reqs.is_empty() } + /// The total number of JSR packages found in the graph. pub fn packages_len(&self) -> usize { self.packages.len() } + /// The total number of dependencies of jsr packages found in the graph. + pub fn package_deps_sum(&self) -> usize { + self + .packages + .iter() + .map(|p| p.1.found_dependencies.len()) + .sum() + } + pub fn add_nv(&mut self, package_req: PackageReq, nv: PackageNv) { let nvs = self .packages_by_name @@ -153,60 +155,21 @@ impl PackageSpecifiers { self.package_reqs.insert(package_req, nv.clone()); } - pub(crate) fn ensure_package( - &mut self, - nv: PackageNv, - manifest_checksum: String, - ) { + pub(crate) fn ensure_package(&mut self, nv: PackageNv) { self.packages.entry(nv).or_insert_with(|| PackageNvInfo { - manifest_checksum, exports: Default::default(), found_dependencies: Default::default(), }); } - pub(crate) fn get_manifest_checksum(&self, nv: &PackageNv) -> Option<&str> { - self.packages.get(nv).map(|p| p.manifest_checksum.as_str()) - } - - pub fn add_manifest_checksum( - &mut self, - nv: PackageNv, - checksum: String, - ) -> Result<(), Box> { - let package = self.packages.get(&nv); - if let Some(package) = package { - if package.manifest_checksum != checksum { - Err(Box::new(PackageManifestIntegrityError { - nv, - actual: checksum, - expected: package.manifest_checksum.clone(), - })) - } else { - Ok(()) - } - } else { - self.packages.insert( - nv, - PackageNvInfo { - manifest_checksum: checksum, - exports: Default::default(), - found_dependencies: Default::default(), - }, - ); - Ok(()) - } - } - /// Gets the dependencies (package constraints) of JSR packages found in the graph. - pub fn packages_with_checksum_and_deps( + pub fn packages_with_deps( &self, - ) -> impl Iterator< - Item = (&PackageNv, &String, impl Iterator), - > { + ) -> impl Iterator)> + { self.packages.iter().map(|(nv, info)| { let deps = info.found_dependencies.iter(); - (nv, &info.manifest_checksum, deps) + (nv, deps) }) } diff --git a/src/source/mod.rs b/src/source/mod.rs index a8027653b..e1019bc1d 100644 --- a/src/source/mod.rs +++ b/src/source/mod.rs @@ -175,43 +175,77 @@ impl fmt::Display for LoaderChecksum { } pub trait Locker { - fn get_checksum(&self, specifier: &ModuleSpecifier) - -> Option; - fn has_checksum(&self, specifier: &ModuleSpecifier) -> bool; - fn set_checksum( + fn get_remote_checksum( + &self, + specifier: &ModuleSpecifier, + ) -> Option; + fn has_remote_checksum(&self, specifier: &ModuleSpecifier) -> bool; + fn set_remote_checksum( &mut self, specifier: &ModuleSpecifier, checksum: LoaderChecksum, ); + + fn get_pkg_manifest_checksum( + &self, + package_nv: &PackageNv, + ) -> Option; + fn set_pkg_manifest_checksum( + &mut self, + package_nv: &PackageNv, + checksum: LoaderChecksum, + ); } #[derive(Debug, Default, Clone)] -pub struct HashMapLocker(HashMap); +pub struct HashMapLocker { + remote: HashMap, + pkg_manifests: HashMap, +} impl HashMapLocker { - pub fn inner(&self) -> &HashMap { - &self.0 + pub fn remote(&self) -> &HashMap { + &self.remote + } + + pub fn pkg_manifests(&self) -> &HashMap { + &self.pkg_manifests } } impl Locker for HashMapLocker { - fn get_checksum( + fn get_remote_checksum( &self, specifier: &ModuleSpecifier, ) -> Option { - self.0.get(specifier).cloned() + self.remote.get(specifier).cloned() } - fn has_checksum(&self, specifier: &ModuleSpecifier) -> bool { - self.0.contains_key(specifier) + fn has_remote_checksum(&self, specifier: &ModuleSpecifier) -> bool { + self.remote.contains_key(specifier) } - fn set_checksum( + fn set_remote_checksum( &mut self, specifier: &ModuleSpecifier, checksum: LoaderChecksum, ) { - self.0.insert(specifier.clone(), checksum); + self.remote.insert(specifier.clone(), checksum); + } + + fn get_pkg_manifest_checksum( + &self, + package_nv: &PackageNv, + ) -> Option { + self.pkg_manifests.get(package_nv).cloned() + } + + fn set_pkg_manifest_checksum( + &mut self, + package_nv: &PackageNv, + checksum: LoaderChecksum, + ) { + self.pkg_manifests.insert(package_nv.clone(), checksum); } } diff --git a/tests/helpers/mod.rs b/tests/helpers/mod.rs index b9ea546fd..69192eedd 100644 --- a/tests/helpers/mod.rs +++ b/tests/helpers/mod.rs @@ -168,7 +168,6 @@ pub struct TestBuilder { workspace_members: Vec, workspace_fast_check: bool, lockfile_jsr_packages: BTreeMap, - verify_and_fill_checksums: bool, } impl TestBuilder { @@ -183,7 +182,6 @@ impl TestBuilder { workspace_members: Default::default(), workspace_fast_check: false, lockfile_jsr_packages: Default::default(), - verify_and_fill_checksums: false, } } @@ -238,17 +236,34 @@ impl TestBuilder { } #[allow(unused)] - pub fn verify_and_fill_checksums(&mut self, value: bool) -> &mut Self { - self.verify_and_fill_checksums = value; + pub fn ensure_locker(&mut self) -> &mut Self { + self.locker.get_or_insert_with(Default::default); self } #[allow(unused)] - pub fn add_checksum(&mut self, specifier: &str, checksum: &str) -> &mut Self { + pub fn add_remote_checksum( + &mut self, + specifier: &str, + checksum: &str, + ) -> &mut Self { let specifier = ModuleSpecifier::parse(specifier).unwrap(); let loader_checksum = LoaderChecksum::new(checksum.to_string()); let checksums = self.locker.get_or_insert_with(Default::default); - checksums.set_checksum(&specifier, loader_checksum); + checksums.set_remote_checksum(&specifier, loader_checksum); + self + } + + #[allow(unused)] + pub fn add_pkg_manifest_checksum( + &mut self, + pkg_nv: &str, + checksum: &str, + ) -> &mut Self { + let pkg_nv = PackageNv::from_str(pkg_nv).unwrap(); + let loader_checksum = LoaderChecksum::new(checksum.to_string()); + let checksums = self.locker.get_or_insert_with(Default::default); + checksums.set_pkg_manifest_checksum(&pkg_nv, loader_checksum); self } diff --git a/tests/specs/graph/checksums/invalid.txt b/tests/specs/graph/checksums/invalid.txt index 8e986cbfd..3d327135f 100644 --- a/tests/specs/graph/checksums/invalid.txt +++ b/tests/specs/graph/checksums/invalid.txt @@ -1,5 +1,5 @@ ~~ { - "checksums": { + "remoteChecksums": { "https://localhost/mod.ts": "invalid" } } ~~ @@ -47,7 +47,7 @@ import 'https://localhost/mod.ts' "redirects": {} } -checksums: +remote checksums: { "https://localhost/mod.ts": "invalid" } diff --git a/tests/specs/graph/checksums/jsr_pkg_fills.txt b/tests/specs/graph/checksums/jsr_pkg_fills.txt new file mode 100644 index 000000000..e181c1cf1 --- /dev/null +++ b/tests/specs/graph/checksums/jsr_pkg_fills.txt @@ -0,0 +1,76 @@ +~~ { + "pkgChecksums": {} +} ~~ +# https://jsr.io/@scope/a/meta.json +{ + "versions": { + "1.0.0": {} + } +} + +# https://jsr.io/@scope/a/1.0.0_meta.json +{ + "exports": { + ".": "./mod.ts" + } +} + +# mod.ts +import 'jsr:@scope/a' + +# https://jsr.io/@scope/a/1.0.0/mod.ts +console.log('HI'); + +# output +{ + "roots": [ + "file:///mod.ts" + ], + "modules": [ + { + "kind": "esm", + "dependencies": [ + { + "specifier": "jsr:@scope/a", + "code": { + "specifier": "jsr:@scope/a", + "span": { + "start": { + "line": 0, + "character": 7 + }, + "end": { + "line": 0, + "character": 21 + } + } + } + } + ], + "size": 22, + "mediaType": "TypeScript", + "specifier": "file:///mod.ts" + }, + { + "kind": "esm", + "size": 19, + "mediaType": "TypeScript", + "specifier": "https://jsr.io/@scope/a/1.0.0/mod.ts" + } + ], + "redirects": { + "jsr:@scope/a": "https://jsr.io/@scope/a/1.0.0/mod.ts" + }, + "packages": { + "@scope/a": "@scope/a@1.0.0" + } +} + +pkg manifest checksums: +{ + "@scope/a@1.0.0": "b4d7d4110ba34c5b8f5f5ef2a59b9b5e502211844bcca5e8abc3860101514531" +} + +Fast check https://jsr.io/@scope/a/1.0.0/mod.ts: + {} + diff --git a/tests/specs/graph/checksums/jsr_pkg_invalid.txt b/tests/specs/graph/checksums/jsr_pkg_invalid.txt new file mode 100644 index 000000000..4d240937b --- /dev/null +++ b/tests/specs/graph/checksums/jsr_pkg_invalid.txt @@ -0,0 +1,70 @@ +~~ { + "pkgChecksums": { + "@scope/a@1.0.0": "invalid" + } +} ~~ +# https://jsr.io/@scope/a/meta.json +{ + "versions": { + "1.0.0": {} + } +} + +# https://jsr.io/@scope/a/1.0.0_meta.json +{ + "exports": { + ".": "./mod.ts" + } +} + +# mod.ts +import 'jsr:@scope/a' + +# https://jsr.io/@scope/a/1.0.0/mod.ts +console.log('HI'); + +# output +{ + "roots": [ + "file:///mod.ts" + ], + "modules": [ + { + "kind": "esm", + "dependencies": [ + { + "specifier": "jsr:@scope/a", + "code": { + "specifier": "jsr:@scope/a", + "span": { + "start": { + "line": 0, + "character": 7 + }, + "end": { + "line": 0, + "character": 21 + } + } + } + } + ], + "size": 22, + "mediaType": "TypeScript", + "specifier": "file:///mod.ts" + }, + { + "specifier": "jsr:@scope/a", + "error": "JSR package version manifest for '@scope/a@1.0.0' failed to load: Integrity check failed.\n\nActual: b4d7d4110ba34c5b8f5f5ef2a59b9b5e502211844bcca5e8abc3860101514531\nExpected: invalid" + } + ], + "redirects": {}, + "packages": { + "@scope/a": "@scope/a@1.0.0" + } +} + +pkg manifest checksums: +{ + "@scope/a@1.0.0": "invalid" +} diff --git a/tests/specs/graph/checksums/jsr_pkg_valid.txt b/tests/specs/graph/checksums/jsr_pkg_valid.txt new file mode 100644 index 000000000..0e81256f7 --- /dev/null +++ b/tests/specs/graph/checksums/jsr_pkg_valid.txt @@ -0,0 +1,78 @@ +~~ { + "pkgChecksums": { + "@scope/a@1.0.0": "b4d7d4110ba34c5b8f5f5ef2a59b9b5e502211844bcca5e8abc3860101514531" + } +} ~~ +# https://jsr.io/@scope/a/meta.json +{ + "versions": { + "1.0.0": {} + } +} + +# https://jsr.io/@scope/a/1.0.0_meta.json +{ + "exports": { + ".": "./mod.ts" + } +} + +# mod.ts +import 'jsr:@scope/a' + +# https://jsr.io/@scope/a/1.0.0/mod.ts +console.log('HI'); + +# output +{ + "roots": [ + "file:///mod.ts" + ], + "modules": [ + { + "kind": "esm", + "dependencies": [ + { + "specifier": "jsr:@scope/a", + "code": { + "specifier": "jsr:@scope/a", + "span": { + "start": { + "line": 0, + "character": 7 + }, + "end": { + "line": 0, + "character": 21 + } + } + } + } + ], + "size": 22, + "mediaType": "TypeScript", + "specifier": "file:///mod.ts" + }, + { + "kind": "esm", + "size": 19, + "mediaType": "TypeScript", + "specifier": "https://jsr.io/@scope/a/1.0.0/mod.ts" + } + ], + "redirects": { + "jsr:@scope/a": "https://jsr.io/@scope/a/1.0.0/mod.ts" + }, + "packages": { + "@scope/a": "@scope/a@1.0.0" + } +} + +pkg manifest checksums: +{ + "@scope/a@1.0.0": "b4d7d4110ba34c5b8f5f5ef2a59b9b5e502211844bcca5e8abc3860101514531" +} + +Fast check https://jsr.io/@scope/a/1.0.0/mod.ts: + {} + diff --git a/tests/specs/graph/checksums/redirect_with_checksum.txt b/tests/specs/graph/checksums/redirect_with_checksum.txt index c7d7e0138..bb9cf2abf 100644 --- a/tests/specs/graph/checksums/redirect_with_checksum.txt +++ b/tests/specs/graph/checksums/redirect_with_checksum.txt @@ -1,5 +1,5 @@ ~~ { - "checksums": { + "remoteChecksums": { "https://localhost/mod.ts": "57fe7492e0def06ef2f6863f5cab3a5d6ec41ac68f2de7c0e27928cc6fef0ccb" } } ~~ @@ -50,7 +50,7 @@ import 'https://localhost/mod.ts' "redirects": {} } -checksums: +remote checksums: { "https://localhost/mod.ts": "57fe7492e0def06ef2f6863f5cab3a5d6ec41ac68f2de7c0e27928cc6fef0ccb" } diff --git a/tests/specs/graph/checksums/redirected_to_invalid.txt b/tests/specs/graph/checksums/redirected_to_invalid.txt index 79e717ca6..1731c861a 100644 --- a/tests/specs/graph/checksums/redirected_to_invalid.txt +++ b/tests/specs/graph/checksums/redirected_to_invalid.txt @@ -1,5 +1,5 @@ ~~ { - "checksums": { + "remoteChecksums": { "https://localhost/redirected.ts": "invalid" } } ~~ @@ -52,7 +52,7 @@ import 'https://localhost/mod.ts' } } -checksums: +remote checksums: { "https://localhost/redirected.ts": "invalid" } diff --git a/tests/specs/graph/checksums/valid.txt b/tests/specs/graph/checksums/valid.txt index 9bd8a3d3d..905f13fe6 100644 --- a/tests/specs/graph/checksums/valid.txt +++ b/tests/specs/graph/checksums/valid.txt @@ -1,5 +1,5 @@ ~~ { - "checksums": { + "remoteChecksums": { "https://localhost/mod.ts": "57fe7492e0def06ef2f6863f5cab3a5d6ec41ac68f2de7c0e27928cc6fef0ccb" } } ~~ @@ -49,7 +49,7 @@ import 'https://localhost/mod.ts' "redirects": {} } -checksums: +remote checksums: { "https://localhost/mod.ts": "57fe7492e0def06ef2f6863f5cab3a5d6ec41ac68f2de7c0e27928cc6fef0ccb" } diff --git a/tests/specs_test.rs b/tests/specs_test.rs index d840b0e96..35ccceb96 100644 --- a/tests/specs_test.rs +++ b/tests/specs_test.rs @@ -86,10 +86,16 @@ fn run_graph_test(test: &CollectedTest) { if let Some(options) = &spec.options { builder.workspace_fast_check(options.workspace_fast_check); builder.fast_check_cache(options.fast_check_cache); - if let Some(checksums) = options.checksums.as_ref() { - builder.verify_and_fill_checksums(true); + if let Some(checksums) = options.remote_checksums.as_ref() { + builder.ensure_locker(); for (specifier, checksum) in checksums { - builder.add_checksum(specifier, checksum); + builder.add_remote_checksum(specifier, checksum); + } + } + if let Some(checksums) = options.pkg_checksums.as_ref() { + builder.ensure_locker(); + for (pkg_nv, checksum) in checksums { + builder.add_pkg_manifest_checksum(pkg_nv, checksum); } } } @@ -103,18 +109,32 @@ fn run_graph_test(test: &CollectedTest) { output_text.push('\n'); // include the checksums if non-empty if let Some(locker) = &result.locker { - let sorted_checksums = locker.inner().iter().collect::>(); - output_text.push_str("\nchecksums:\n"); - output_text - .push_str(&serde_json::to_string_pretty(&sorted_checksums).unwrap()); - output_text.push('\n'); + { + let sorted_checksums = locker.remote().iter().collect::>(); + if !sorted_checksums.is_empty() { + output_text.push_str("\nremote checksums:\n"); + output_text + .push_str(&serde_json::to_string_pretty(&sorted_checksums).unwrap()); + output_text.push('\n'); + } + } + { + let sorted_checksums = + locker.pkg_manifests().iter().collect::>(); + if !sorted_checksums.is_empty() { + output_text.push_str("\npkg manifest checksums:\n"); + output_text + .push_str(&serde_json::to_string_pretty(&sorted_checksums).unwrap()); + output_text.push('\n'); + } + } } // include the list of jsr dependencies let jsr_deps = result .graph .packages - .packages_with_checksum_and_deps() - .map(|(k, _checksum, deps)| { + .packages_with_deps() + .map(|(k, deps)| { (k.to_string(), { let mut deps = deps.map(|d| d.to_string()).collect::>(); deps.sort(); @@ -301,7 +321,10 @@ fn run_symbol_test(test: &CollectedTest) { pub struct SpecOptions { #[serde(default)] #[serde(skip_serializing_if = "Option::is_none")] - pub checksums: Option>, + pub remote_checksums: Option>, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + pub pkg_checksums: Option>, #[serde(default)] #[serde(skip_serializing_if = "is_false")] pub workspace_fast_check: bool, @@ -328,7 +351,7 @@ impl Spec { let mut text = String::new(); if let Some(options) = &self.options { text.push_str("~~ "); - if options.checksums.is_some() { + if options.remote_checksums.is_some() || options.pkg_checksums.is_some() { text.push_str(&serde_json::to_string_pretty(options).unwrap()); } else { text.push_str(&serde_json::to_string(options).unwrap()); @@ -382,7 +405,7 @@ impl Spec { .find(|f| f.url() == meta_file) .unwrap_or_else(|| panic!("Could not find in specs: {}", meta_file)); let mut meta_value = serde_json::from_str::< - HashMap, + BTreeMap, >(&meta_file.text) .unwrap(); let manifest = meta_value @@ -404,10 +427,10 @@ impl Spec { pub fn get_jsr_checksums( &self, - ) -> HashMap> { - let mut checksums_by_package: HashMap< + ) -> BTreeMap> { + let mut checksums_by_package: BTreeMap< PackageNv, - HashMap, + BTreeMap, > = Default::default(); for file in &self.files { if let Some(nv) =