Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 0 additions & 11 deletions core/src/commands/lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,16 +163,6 @@ pub fn do_lock_projects<
field: IncompleteField::Meta,
})
})?;
// let canonical_digest = project
// .checksum_canonical_hex()
// .map_err(LockProjectError::InputProjectCanonicalizationError)?
// .ok_or_else(|| {
// LockProjectError::LockError(LockError::IncompleteProject {
// project_label: named_project_label,
// field: IncompleteField::CanonicalDigest,
// })
// })?;

let sources = project
.sources(ctx)
.map_err(LockProjectError::InputProjectError)?;
Expand All @@ -186,7 +176,6 @@ pub fn do_lock_projects<
identifiers: identifiers
.map(|ids| ids.into_iter().map(|id| id.into_string()).collect())
.unwrap_or_default(),
// checksum: canonical_digest,
sources,
usages: info
.usage
Expand Down
45 changes: 43 additions & 2 deletions core/src/commands/lock_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
use std::collections::HashMap;

use crate::{
commands::lock::{LockError, do_lock_extend},
lock::{Lock, Project},
commands::lock::{LockError, do_lock_extend, do_lock_projects},
lock::{Lock, Project, Source},
model::{InterchangeProjectInfoRaw, InterchangeProjectMetadataRaw},
project::memory::InMemoryProject,
resolve::null::NullResolver,
};

Expand Down Expand Up @@ -46,3 +48,42 @@ fn lock_export_conflict() {

assert!(matches!(res, Err(LockError::NameCollision(_))));
}

#[test]
fn lock_preserves_project_publisher() {
let mut project = InMemoryProject::from_info_meta(
InterchangeProjectInfoRaw {
name: "published_project".into(),
publisher: Some("Acme Labs".into()),
version: "1.2.3".into(),
description: None,
license: None,
maintainer: vec![],
website: None,
topic: vec![],
usage: vec![],
},
InterchangeProjectMetadataRaw {
index: Default::default(),
created: "2026-01-01T00:00:00Z".into(),
metamodel: None,
includes_derived: None,
includes_implied: None,
checksum: None,
},
);
project.nominal_sources = vec![Source::Editable {
editable: ".".into(),
}];

let lock = do_lock_projects(
[(None, &project)],
NullResolver {},
&HashMap::new(),
&Default::default(),
)
.unwrap()
.lock;

assert_eq!(lock.projects[0].publisher.as_deref(), Some("Acme Labs"));
}
56 changes: 19 additions & 37 deletions core/src/env/local_directory/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,19 +104,17 @@ impl LocalDirectoryEnvironment {
// TODO: Integrate the updating of editable metadata into `WriteEnvironment` trait.
// This will likely require updating it to support
// multiple identifiers per project.
/// Precondition: sync has completed, i.e. projects from `lock` are installed
/// by `self.put_project()`.
/// Call is idempotent.
/// Does not update metadata file.
// TODO: what to do if lock does not contain projects that are present in env:
// - workspace: env also has to remove it, the project was deleted/renamed
// - editable: unclear; it could be (re)moved/renamed, but also it could be that
// workspace just no longer depends on it, so it's absent from lock.
/// Updates list of projects present in env metadata, but not installed in the
/// environment (for now this includes only `editable` projects). All previous
/// non-installed projects are removed first.
/// To install a project in the environment, use `put_project()`.
/// Call is idempotent. Does not update metadata file
pub fn merge_lock(&mut self, lock: &Lock, ws: Option<&Workspace>) {
self.metadata.projects.retain(Self::is_installed);
for project in &lock.projects {
// Projects that are installed in the environment are ignored, so only
// editable (and workspace, which are a subset of editable) projects have to be added
if let [Source::Editable { editable }, ..] = project.sources.as_slice() {
if let Some(Source::Editable { editable }) = project.sources.first() {
let usages = project
.usages
.iter()
Expand All @@ -126,34 +124,18 @@ impl LocalDirectoryEnvironment {
let workspace_member = ws
.map(|w| w.projects().iter().any(|p| p.path.as_str() == editable))
.unwrap_or_default();
// This is called once per `sync`, so has to be idempotent
if let Some(existing) = self
.metadata
.find_project_version_any_mut(&project.identifiers, &project.version)
{
assert_eq!(existing.workspace, workspace_member);
assert!(existing.editable);

for iri in &project.identifiers {
if !existing.identifiers.contains(iri) {
existing.identifiers.push(iri.to_owned());
}
}
existing.path = editable.as_str().into();
existing.usages = usages;
} else {
self.metadata.projects.push(EnvProject {
publisher: project.publisher.to_owned(),
name: project.name.to_owned(),
version: project.version.to_owned(),
path: editable.as_str().into(),
identifiers: project.identifiers.to_owned(),
usages,
editable: true,
workspace: workspace_member,
checksum: None,
});
}

self.metadata.projects.push(EnvProject {
publisher: project.publisher.to_owned(),
name: project.name.to_owned(),
version: project.version.to_owned(),
path: editable.as_str().into(),
identifiers: project.identifiers.to_owned(),
usages,
editable: true,
workspace: workspace_member,
checksum: None,
});
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions core/src/lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,9 @@ pub fn hash_str(val: &str) -> StrHash {
impl Project {
pub fn to_toml(&self) -> Table {
let mut table = Table::new();
if let Some(publisher) = &self.publisher {
table.insert("publisher", value(publisher));
}
table.insert("name", value(&self.name));
table.insert("version", value(&self.version));
let exports = multiline_array(self.exports.iter().map(Value::from));
Expand Down
6 changes: 4 additions & 2 deletions core/src/lock_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ fn many_projects_to_toml() {
vec![
Project {
name: "One".to_string(),
publisher: None,
publisher: Some("Pub 1".to_string()),
version: "0.0.1".to_string(),
exports: vec![],
identifiers: vec![],
Expand All @@ -150,7 +150,7 @@ fn many_projects_to_toml() {
},
Project {
name: "Three".to_string(),
publisher: None,
publisher: Some("Pub 3".to_string()),
version: "0.0.3".to_string(),
exports: vec![],
identifiers: vec![],
Expand All @@ -160,6 +160,7 @@ fn many_projects_to_toml() {
],
r#"
[[project]]
publisher = "Pub 1"
name = "One"
version = "0.0.1"

Expand All @@ -168,6 +169,7 @@ name = "Two"
version = "0.0.2"

[[project]]
publisher = "Pub 3"
name = "Three"
version = "0.0.3"
"#,
Expand Down
49 changes: 26 additions & 23 deletions core/src/project/local_kpar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,15 @@ impl LocalKParProjectRaw {
&self.archive_path
}

/// Returns project root in archive. If `None`, project is at the
/// root of the archive
pub fn project_root_in_archive(&self) -> Option<&Utf8UnixPath> {
// TODO: maybe it'd be worth enforcing that Some(p) => p is not empty?
// Would simplify a bunch of places which currently must check
// both
self.root.as_deref()
}

/// Build a KPAR archive from `from`.
///
/// `extra_files` are added to the archive alongside the project's source
Expand Down Expand Up @@ -461,6 +470,23 @@ impl LocalKParProjectRaw {
Ok(wrapfs::metadata(&self.archive_path)?.len())
}

pub fn digest_sha256(&self) -> Result<String, LocalKParError> {
let mut file = self.open_archive_file()?;
let mut buf = [0; 1024];
let mut hasher = Sha256::new();
loop {
let count = file
.read(&mut buf)
.map_err(|e| FsIoError::ReadFile(self.archive_path.clone(), e))?;
if count > 0 {
hasher.update(&buf[..count]);
} else {
break;
}
}
Ok(lowercase_hex(hasher.finalize()))
}

fn open_archive_file(&self) -> Result<fs::File, LocalKParError> {
Ok(wrapfs::File::open(&self.archive_path)?)
}
Expand Down Expand Up @@ -502,23 +528,6 @@ impl LocalKParProjectRaw {
))),
}
}

pub fn digest_sha256(&self) -> Result<String, LocalKParError> {
let mut file = self.open_archive_file()?;
let mut buf = [0; 1024];
let mut hasher = Sha256::new();
loop {
let count = file
.read(&mut buf)
.map_err(|e| FsIoError::ReadFile(self.archive_path.clone(), e))?;
if count > 0 {
hasher.update(&buf[..count]);
} else {
break;
}
}
Ok(lowercase_hex(hasher.finalize()))
}
}

// NOTE: Current implementation keeps re-opening the archive file. This appears to
Expand Down Expand Up @@ -575,12 +584,6 @@ impl ProjectRead for LocalKParProjectRaw {
.get_relative(&mut archive, path)
.map_err(|(p, e)| ZipArchiveError::NamedFileMeta(p.into_string().into(), e))?;

// let idx = path_index(self.root.as_deref(), &mut archive, &path)?;

// let mut zip_file = archive
// .by_index(idx)
// .map_err(|e| ZipArchiveError::NamedFileMeta(path.as_ref().as_str().into(), e))?;

std::io::copy(&mut zip_file, &mut tmp_file)
.map_err(|e| FsIoError::WriteFile(tmp_file_path.clone(), e))?;
}
Expand Down
Loading
Loading