fix: release Dever 0.1.6 with shared runtime assets and unbounded workers

This commit is contained in:
Codex committed 2026-10-12 00:13:27 +00:00
1 parent a900d7e8db
commit 5589b657db
17 files changed
+357 -49

No files matched your search

+2 -2
View File
@@ -858,7 +858,7 @@ impl InstalledRegistry {
fn signed_artifact(&self, relative: &Path) -> Result<Vec<u8>, String> {
let name = relative.to_str().ok_or("runtime path is not UTF-8")?;
self.resources.read(name)
self.resources.read_shared(name)
}
fn process_runtime_resources(
@@ -868,7 +868,7 @@ impl InstalledRegistry {
let prefix = format!("process-runtime/{}/", target.platform());
let mut resources = Vec::new();
let mut files = Vec::new();
for artifact in self.resources.artifacts(&prefix)? {
for artifact in self.resources.catalog_artifacts(&prefix) {
let Some(relative) = artifact.path.strip_prefix(&prefix) else {
continue;
};
@@ -270,6 +270,92 @@ impl SignedResources {
Ok(bytes)
}
pub fn read_shared(&self, logical_path: &str) -> Result<Vec<u8>, String> {
if self
.manifest
.artifacts
.iter()
.any(|artifact| artifact.path == logical_path)
{
return self.read(logical_path);
}
let artifact = self
.manifest
.artifacts
.iter()
.chain(
self.manifest
.extensions
.iter()
.flat_map(|extension| &extension.artifacts),
)
.find(|artifact| artifact.path == logical_path)
.ok_or_else(|| format!("signed release does not declare resource '{logical_path}'"))?;
super::service::resource_get(
&self.layout,
&self.manifest.version,
&self.catalog,
self.manifest
.extensions
.iter()
.find(|extension| {
extension
.artifacts
.iter()
.any(|artifact| artifact.path == logical_path)
})
.map(Extension::id),
&super::ArtifactReceipt {
sha256: artifact.sha256.clone(),
bytes: artifact.bytes,
},
)
}
pub(super) fn open_requested(
&self,
catalog: &str,
extension: Option<&ExtensionId>,
expected: &super::ArtifactReceipt,
) -> Result<fs::File, String> {
if catalog != self.catalog {
return Err("signed resource catalog differs from the installed release".into());
}
let artifacts = match extension {
Some(id) => &self.extension(id)?.artifacts,
None => &self.manifest.artifacts,
};
let artifact = artifacts
.iter()
.find(|artifact| artifact.sha256 == expected.sha256 && artifact.bytes == expected.bytes)
.ok_or("signed release does not declare the requested resource identity")?;
let path = self.path(&artifact.path)?;
let file = fs::File::open(path)
.map_err(|error| format!("cannot open signed resource: {error}"))?;
let metadata = file
.metadata()
.map_err(|error| format!("cannot inspect signed resource: {error}"))?;
if !metadata.is_file() || metadata.len() != artifact.bytes {
return Err("signed resource changed while opening".into());
}
Ok(file)
}
pub fn catalog_artifacts(&self, prefix: &str) -> Vec<Artifact> {
self.manifest
.artifacts
.iter()
.chain(
self.manifest
.extensions
.iter()
.flat_map(|extension| &extension.artifacts),
)
.filter(|artifact| artifact.path.starts_with(prefix))
.cloned()
.collect()
}
pub fn artifacts(&self, prefix: &str) -> Result<Vec<Artifact>, String> {
let mut artifacts = Vec::new();
for artifact in self
+90 -8
View File
@@ -105,6 +105,12 @@ enum Operation {
version: Version,
extension: super::ExtensionId,
},
ResourceGet {
version: Version,
catalog: String,
extension: Option<super::ExtensionId>,
artifact: ArtifactReceipt,
},
Compile {
bytes: u64,
},
@@ -361,14 +367,7 @@ pub fn artifact_get_optional(
return Ok(None);
}
require_artifact(response, &expected)?;
let mut bytes = vec![0; expected.bytes as usize];
stream
.read_exact(&mut bytes)
.map_err(|error| format!("deverd artifact download is incomplete: {error}"))?;
if hex(&Sha256::digest(&bytes)) != expected.sha256 {
return Err("deverd artifact download failed SHA-256 verification".into());
}
Ok(Some(bytes))
read_artifact(&mut stream, &expected).map(Some)
}
#[cfg(not(unix))]
{
@@ -401,6 +400,59 @@ pub fn ensure_extension(
}
}
pub(super) fn resource_get(
layout: &Layout,
version: &Version,
catalog: &str,
extension: Option<super::ExtensionId>,
expected: &ArtifactReceipt,
) -> Result<Vec<u8>, String> {
#[cfg(unix)]
{
let (mut stream, token) = connect(layout)?;
stream
.set_read_timeout(Some(std::time::Duration::from_secs(30)))
.map_err(|error| format!("cannot configure signed resource IPC timeout: {error}"))?;
write_frame(
&mut stream,
&Request {
token,
operation: Operation::ResourceGet {
version: version.clone(),
catalog: catalog.into(),
extension,
artifact: expected.clone(),
},
installed: Vec::new(),
},
)?;
require_artifact(read_frame(&mut stream)?, expected)?;
read_artifact(&mut stream, expected)
}
#[cfg(not(unix))]
{
let _ = (layout, version, catalog, extension, expected);
Err("deverd signed resource IPC is not implemented for this platform".into())
}
}
#[cfg(unix)]
fn read_artifact(
stream: &mut std::os::unix::net::UnixStream,
expected: &ArtifactReceipt,
) -> Result<Vec<u8>, String> {
let length = usize::try_from(expected.bytes)
.map_err(|_| "deverd artifact is too large for this platform")?;
let mut bytes = vec![0; length];
stream
.read_exact(&mut bytes)
.map_err(|error| format!("deverd artifact download is incomplete: {error}"))?;
if hex(&Sha256::digest(&bytes)) != expected.sha256 {
return Err("deverd artifact download failed SHA-256 verification".into());
}
Ok(bytes)
}
fn require_artifact(response: Response, expected: &ArtifactReceipt) -> Result<(), String> {
if !response.ok {
return Err(response
@@ -597,6 +649,36 @@ fn handle(
Err(error) => failure(error),
};
}
Operation::ResourceGet {
version,
catalog,
extension,
artifact,
} => {
let file = match super::SignedResources::load(layout, &version).and_then(|resources| {
resources.open_requested(&catalog, extension.as_ref(), &artifact)
}) {
Ok(file) => file,
Err(error) => return failure(error),
};
let length = artifact.bytes;
if let Err(error) = write_frame(stream, &artifact_response(artifact)) {
return failure(error);
}
match std::io::copy(&mut file.take(length), stream) {
Ok(copied) if copied == length => {}
Ok(_) => return failure("deverd signed resource transfer is truncated".into()),
Err(error) => {
return failure(format!("deverd signed resource transfer failed: {error}"));
}
}
return Response {
ok: true,
status: None,
artifact: None,
error: None,
};
}
Operation::Compile { bytes } => {
return match compile::serve(layout, stream, bytes) {
Ok(()) => Response {