Update public bundle metadata contract

This commit is contained in:
Michel Paulissen 2026-07-04 10:22:53 +02:00
parent c4d8d94f49
commit c4141e0dee
6 changed files with 204 additions and 5 deletions

View file

@ -4316,8 +4316,11 @@ fn bundle_inspection(args: BundleInspectArgs, cwd: PathBuf) -> Result<BundleInsp
let identity_inputs = BundleIdentityInputs {
wasm_code: wasm_source_proxy_digest(&selected_inputs),
task_abi: task_abi_digest(&model),
entrypoints: model.entrypoints.keys().cloned().collect(),
default_entrypoint: model.default_entrypoint.clone(),
environments: model.environments.clone(),
source_provider_manifest: source_provider_manifest.digest.clone(),
source_transfer_policy: source_provider_manifest.transfer_policy.clone(),
selected_inputs,
};
let pre_schedule_diagnostics = pre_schedule_diagnostics(
@ -5951,12 +5954,18 @@ mod tests {
fn bundle_inspect_discovers_environments_selected_inputs_and_source_providers() {
let temp = tempfile::tempdir().unwrap();
fs::create_dir_all(temp.path().join("envs/linux")).unwrap();
fs::create_dir_all(temp.path().join("envs/docker")).unwrap();
fs::create_dir_all(temp.path().join("src")).unwrap();
fs::write(
temp.path().join("envs/linux/Containerfile"),
"FROM alpine\n",
)
.unwrap();
fs::write(
temp.path().join("envs/docker/Dockerfile"),
"FROM debian:stable-slim\n",
)
.unwrap();
fs::write(temp.path().join("Cargo.toml"), "[package]\nname='demo'\n").unwrap();
fs::write(temp.path().join("src/main.rs"), "fn main() {}\n").unwrap();
@ -6003,7 +6012,62 @@ mod tests {
.transfer_policy
.default_full_repo_tarball
);
assert_eq!(inspection.metadata.environments[0].name, "linux");
assert!(inspection
.metadata
.environments
.iter()
.any(|environment| environment.name == "linux"));
assert!(inspection
.metadata
.environments
.iter()
.any(|environment| environment.name == "docker"));
assert_eq!(
inspection.metadata.source_metadata.source_provider_manifest,
inspection.source_provider_manifest.digest
);
assert!(
inspection
.metadata
.source_metadata
.transfer_policy
.local_source_bytes_remain_node_local
);
assert!(
!inspection
.metadata
.source_metadata
.transfer_policy
.coordinator_receives_source_bytes_by_default
);
assert!(
!inspection
.metadata
.source_metadata
.transfer_policy
.default_full_repo_tarball
);
assert!(inspection
.metadata
.task_metadata
.entrypoints
.contains(&"build".to_owned()));
assert_eq!(
inspection.metadata.task_metadata.default_entrypoint,
"build"
);
assert_eq!(
inspection.metadata.task_metadata.task_abi,
task_abi_digest(&ProjectModel::discover_without_config(temp.path()).unwrap())
);
assert!(inspection
.metadata
.wasm_code
.as_str()
.starts_with("sha256:"));
assert!(inspection.metadata.debug_metadata.available);
assert!(inspection.metadata.debug_metadata.source_level_breakpoints);
assert!(inspection.metadata.debug_metadata.variables_pane_supported);
assert!(!inspection.metadata.embeds_full_container_images);
assert!(inspection
.metadata
@ -7567,6 +7631,33 @@ mod tests {
.as_str()
.unwrap()
.starts_with("sha256:"));
assert!(report["bundle"]["metadata"]["wasm_code"]
.as_str()
.unwrap()
.starts_with("sha256:"));
assert_eq!(
report["bundle"]["metadata"]["task_metadata"]["default_entrypoint"],
"build"
);
assert!(report["bundle"]["metadata"]["task_metadata"]["entrypoints"]
.as_array()
.unwrap()
.iter()
.any(|entrypoint| entrypoint == "release"));
assert_eq!(
report["bundle"]["metadata"]["source_metadata"]["transfer_policy"]
["coordinator_receives_source_bytes_by_default"],
false
);
assert_eq!(
report["bundle"]["metadata"]["source_metadata"]["transfer_policy"]
["default_full_repo_tarball"],
false
);
assert_eq!(
report["bundle"]["metadata"]["debug_metadata"]["dap_virtual_process"],
true
);
assert_eq!(report["status"], "built");
assert_eq!(report["scheduled_work"], false);
}

View file

@ -1,6 +1,6 @@
use serde::{Deserialize, Serialize};
use crate::{Digest, EnvironmentResource};
use crate::{Digest, EnvironmentResource, SourceTransferPolicy};
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct SelectedInput {
@ -12,19 +12,49 @@ pub struct SelectedInput {
pub struct BundleIdentityInputs {
pub wasm_code: Digest,
pub task_abi: Digest,
pub entrypoints: Vec<String>,
pub default_entrypoint: String,
pub environments: Vec<EnvironmentResource>,
pub source_provider_manifest: Digest,
pub source_transfer_policy: SourceTransferPolicy,
pub selected_inputs: Vec<SelectedInput>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct BundleMetadata {
pub identity: Digest,
pub wasm_code: Digest,
pub task_metadata: BundleTaskMetadata,
pub source_metadata: BundleSourceMetadata,
pub debug_metadata: BundleDebugMetadata,
pub environments: Vec<EnvironmentResource>,
pub selected_inputs: Vec<SelectedInput>,
pub embeds_full_container_images: bool,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct BundleTaskMetadata {
pub task_abi: Digest,
pub entrypoints: Vec<String>,
pub default_entrypoint: String,
pub boundary: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct BundleSourceMetadata {
pub source_provider_manifest: Digest,
pub transfer_policy: SourceTransferPolicy,
pub selected_inputs: Vec<SelectedInput>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct BundleDebugMetadata {
pub available: bool,
pub source_level_breakpoints: bool,
pub dap_virtual_process: bool,
pub variables_pane_supported: bool,
}
impl BundleIdentityInputs {
pub fn identity(&self) -> Digest {
let mut parts = vec![
@ -32,8 +62,16 @@ impl BundleIdentityInputs {
self.wasm_code.as_str().as_bytes().to_vec(),
self.task_abi.as_str().as_bytes().to_vec(),
self.source_provider_manifest.as_str().as_bytes().to_vec(),
self.default_entrypoint.as_bytes().to_vec(),
format!("{:?}", self.source_transfer_policy).into_bytes(),
];
let mut entrypoints = self.entrypoints.clone();
entrypoints.sort();
for entrypoint in entrypoints {
parts.push(entrypoint.into_bytes());
}
let mut environments = self.environments.clone();
environments.sort_by(|left, right| left.name.cmp(&right.name));
for environment in environments {
@ -53,8 +91,29 @@ impl BundleIdentityInputs {
}
pub fn inspectable_metadata(&self) -> BundleMetadata {
let mut entrypoints = self.entrypoints.clone();
entrypoints.sort();
BundleMetadata {
identity: self.identity(),
wasm_code: self.wasm_code.clone(),
task_metadata: BundleTaskMetadata {
task_abi: self.task_abi.clone(),
entrypoints,
default_entrypoint: self.default_entrypoint.clone(),
boundary: "disasmer_task_exports".to_owned(),
},
source_metadata: BundleSourceMetadata {
source_provider_manifest: self.source_provider_manifest.clone(),
transfer_policy: self.source_transfer_policy.clone(),
selected_inputs: self.selected_inputs.clone(),
},
debug_metadata: BundleDebugMetadata {
available: true,
source_level_breakpoints: true,
dap_virtual_process: true,
variables_pane_supported: true,
},
environments: self.environments.clone(),
selected_inputs: self.selected_inputs.clone(),
embeds_full_container_images: false,
@ -86,8 +145,11 @@ mod tests {
let base = BundleIdentityInputs {
wasm_code: Digest::sha256("wasm"),
task_abi: Digest::sha256("abi"),
entrypoints: vec!["build".to_owned()],
default_entrypoint: "build".to_owned(),
environments: vec![env("recipe-a")],
source_provider_manifest: Digest::sha256("source"),
source_transfer_policy: SourceTransferPolicy::local_first_snapshot_chunks(),
selected_inputs: vec![],
};
let mut changed = base.clone();
@ -101,8 +163,11 @@ mod tests {
let inputs = BundleIdentityInputs {
wasm_code: Digest::sha256("wasm"),
task_abi: Digest::sha256("abi"),
entrypoints: vec!["build".to_owned(), "test".to_owned()],
default_entrypoint: "build".to_owned(),
environments: vec![env("recipe")],
source_provider_manifest: Digest::sha256("source"),
source_transfer_policy: SourceTransferPolicy::local_first_snapshot_chunks(),
selected_inputs: vec![SelectedInput {
path: "inputs/config.json".to_owned(),
digest: Digest::sha256("config"),
@ -111,6 +176,19 @@ mod tests {
let metadata = inputs.inspectable_metadata();
assert!(metadata.wasm_code.as_str().starts_with("sha256:"));
assert_eq!(metadata.task_metadata.default_entrypoint, "build");
assert!(metadata
.task_metadata
.entrypoints
.contains(&"test".to_owned()));
assert!(metadata.debug_metadata.dap_virtual_process);
assert!(
metadata
.source_metadata
.transfer_policy
.local_source_bytes_remain_node_local
);
assert_eq!(metadata.environments.len(), 1);
assert!(!metadata.embeds_full_container_images);
}

View file

@ -26,7 +26,10 @@ pub use auth::{
CliLoginFlow, CredentialKind, EnrollmentError, EnrollmentGrant, IdentityKind, NodeCredential,
PublicKeyIdentity, Scope,
};
pub use bundle::{BundleIdentityInputs, BundleMetadata, SelectedInput};
pub use bundle::{
BundleDebugMetadata, BundleIdentityInputs, BundleMetadata, BundleSourceMetadata,
BundleTaskMetadata, SelectedInput,
};
pub use capability::{Capability, CapabilityReportError, EnvironmentBackend, NodeCapabilities, Os};
pub use checkpoint::{
CheckpointBoundary, CompatibilityFailure, RestartDecision, RestartPolicy, RestartRequest,