Public release release-ff7f847f143d

Source commit: ff7f847f143dc67864bed68a86cf259114384bd5

Public tree identity: sha256:bb7dcd2ac78bdbad2f4eba0f49be649d446d67f96f4eb2796941c026412fc032
This commit is contained in:
Clusterflux release 2026-07-17 09:33:47 +02:00
commit 70319cde15
210 changed files with 78958 additions and 0 deletions

View file

@ -0,0 +1,322 @@
use std::path::{Path, PathBuf};
use std::process::Command;
use anyhow::{bail, Context, Result};
use clusterflux_core::Digest;
use serde_json::{json, Value};
use wasmparser::{Parser, Payload};
use crate::errors::cli_error_summary_for_category;
use crate::{bundle_inspection, BuildArgs, BundleInspectArgs};
pub(crate) fn build_report(args: BuildArgs, cwd: PathBuf) -> Result<Value> {
let inspection = bundle_inspection(
BundleInspectArgs {
project: args.project.clone(),
source_provider: args.source_provider.clone(),
disabled_source_providers: args.disabled_source_providers.clone(),
json: true,
},
cwd,
)?;
let diagnostics = inspection.pre_schedule_diagnostics.clone();
let blocking_diagnostic = diagnostics
.iter()
.find(|diagnostic| diagnostic.severity == "error")
.cloned();
if let Some(diagnostic) = blocking_diagnostic {
let machine_category = match diagnostic.category.as_str() {
"environment" => "environment",
"source_provider" | "capability" => "capability",
_ => "unknown",
};
return Ok(json!({
"command": "build",
"status": "blocked_before_schedule",
"bundle": inspection,
"diagnostics": diagnostics,
"contains_full_repository_upload": false,
"content_addressed": false,
"debug_metadata_available": false,
"scheduled_work": false,
"machine_error": cli_error_summary_for_category(machine_category, &diagnostic.message),
}));
}
let mut wasm = compile_project_wasm(&inspection.project)?;
let environment_manifest = serde_json::to_vec(&inspection.metadata.environments)?;
append_custom_section(
&mut wasm.bytes,
"clusterflux.environments",
&environment_manifest,
);
let bundle_digest = Digest::sha256(&wasm.bytes);
let task_descriptors = descriptor_records(&wasm.bytes, "clusterflux.tasks")?;
let entrypoint_descriptors = descriptor_records(&wasm.bytes, "clusterflux.entrypoints")?;
if task_descriptors.is_empty() {
bail!(
"compiled Wasm module contains no #[clusterflux::task] descriptors; annotate at least one exported task"
);
}
if entrypoint_descriptors.is_empty() {
bail!(
"compiled Wasm module contains no #[clusterflux::main] descriptors; annotate at least one entrypoint"
);
}
let output = args.output.unwrap_or_else(|| {
inspection.project.join(".clusterflux/build").join(
bundle_digest
.as_str()
.trim_start_matches("sha256:")
.get(..16)
.unwrap_or("bundle"),
)
});
let bundle_artifact = write_bundle(
&output,
&wasm,
&bundle_digest,
&inspection,
&task_descriptors,
&entrypoint_descriptors,
)?;
Ok(json!({
"command": "build",
"status": "built",
"bundle": inspection,
"bundle_artifact": bundle_artifact,
"diagnostics": diagnostics,
"contains_full_repository_upload": false,
"content_addressed": true,
"debug_metadata_available": true,
"scheduled_work": false,
}))
}
fn append_custom_section(module: &mut Vec<u8>, name: &str, data: &[u8]) {
let mut section = Vec::new();
encode_unsigned_leb(name.len() as u64, &mut section);
section.extend_from_slice(name.as_bytes());
section.extend_from_slice(data);
module.push(0);
encode_unsigned_leb(section.len() as u64, module);
module.extend_from_slice(&section);
}
fn encode_unsigned_leb(mut value: u64, output: &mut Vec<u8>) {
loop {
let mut byte = (value & 0x7f) as u8;
value >>= 7;
if value != 0 {
byte |= 0x80;
}
output.push(byte);
if value == 0 {
break;
}
}
}
struct CompiledWasm {
bytes: Vec<u8>,
package: String,
target: String,
source_path: PathBuf,
}
fn compile_project_wasm(project: &Path) -> Result<CompiledWasm> {
let manifest = project.join("Cargo.toml");
let metadata_output = Command::new("cargo")
.args([
"metadata",
"--format-version",
"1",
"--no-deps",
"--manifest-path",
])
.arg(&manifest)
.output()
.with_context(|| format!("failed to run cargo metadata for {}", manifest.display()))?;
if !metadata_output.status.success() {
bail!(
"cargo metadata failed for {}: {}",
manifest.display(),
String::from_utf8_lossy(&metadata_output.stderr).trim()
);
}
let metadata: Value = serde_json::from_slice(&metadata_output.stdout)?;
let canonical_manifest = std::fs::canonicalize(&manifest)?;
let package = metadata["packages"]
.as_array()
.and_then(|packages| {
packages.iter().find(|package| {
package["manifest_path"]
.as_str()
.and_then(|path| std::fs::canonicalize(path).ok())
.as_ref()
== Some(&canonical_manifest)
})
})
.context("cargo metadata did not return the requested project package")?;
let package_name = package["name"]
.as_str()
.context("cargo package name missing")?;
let target = package["targets"]
.as_array()
.and_then(|targets| {
targets.iter().find(|target| {
target["crate_types"]
.as_array()
.is_some_and(|types| types.iter().any(|kind| kind == "cdylib"))
})
})
.context("Clusterflux project library must include crate-type = [\"cdylib\"]")?;
let target_name = target["name"]
.as_str()
.context("cargo target name missing")?;
let build = Command::new("cargo")
// The MVP transports one Wasm bundle in a bounded control frame. These are
// ordinary Cargo release-profile settings, applied only to the guest build,
// that keep the product SDK/runtime inside that accepted boundary.
.env("CARGO_PROFILE_RELEASE_OPT_LEVEL", "z")
.env("CARGO_PROFILE_RELEASE_LTO", "thin")
.env("CARGO_PROFILE_RELEASE_CODEGEN_UNITS", "1")
.args([
"build",
"--quiet",
"--release",
"--target",
"wasm32-unknown-unknown",
"--lib",
"--manifest-path",
])
.arg(&manifest)
.output()
.with_context(|| format!("failed to compile {} to Wasm", manifest.display()))?;
if !build.status.success() {
bail!(
"Wasm bundle compilation failed for {}: {}",
manifest.display(),
String::from_utf8_lossy(&build.stderr).trim()
);
}
let target_directory = metadata["target_directory"]
.as_str()
.context("cargo target_directory missing")?;
let source_path = Path::new(target_directory)
.join("wasm32-unknown-unknown/release")
.join(format!("{}.wasm", target_name.replace('-', "_")));
let bytes = std::fs::read(&source_path)
.with_context(|| format!("compiled Wasm module missing at {}", source_path.display()))?;
Ok(CompiledWasm {
bytes,
package: package_name.to_owned(),
target: target_name.to_owned(),
source_path,
})
}
fn descriptor_records(module: &[u8], section_name: &str) -> Result<Vec<Value>> {
let mut records: Vec<Value> = Vec::new();
for payload in Parser::new(0).parse_all(module) {
let Payload::CustomSection(section) = payload? else {
continue;
};
if section.name() != section_name {
continue;
}
for record in section
.data()
.split(|byte| *byte == b'\n' || *byte == 0)
.filter(|record| !record.is_empty())
{
records.push(serde_json::from_slice(record).with_context(|| {
format!("invalid descriptor record in Wasm custom section {section_name}")
})?);
}
}
records.sort_by(|left, right| left["name"].as_str().cmp(&right["name"].as_str()));
Ok(records)
}
fn write_bundle(
output: &Path,
wasm: &CompiledWasm,
bundle_digest: &Digest,
inspection: &crate::bundle::BundleInspection,
tasks: &[Value],
entrypoints: &[Value],
) -> Result<Value> {
std::fs::create_dir_all(output)?;
let module_path = output.join("module.wasm");
let task_path = output.join("task-descriptors.json");
let entrypoint_path = output.join("entrypoints.json");
let environment_path = output.join("environments.json");
let source_path = output.join("source-provider.json");
let vfs_path = output.join("vfs-seed.json");
let debug_path = output.join("debug-metadata.json");
let manifest_path = output.join("manifest.json");
std::fs::write(&module_path, &wasm.bytes)?;
write_json(&task_path, &json!(tasks))?;
write_json(&entrypoint_path, &json!(entrypoints))?;
write_json(&environment_path, &json!(inspection.metadata.environments))?;
write_json(&source_path, &json!(inspection.source_provider_manifest))?;
write_json(
&vfs_path,
&json!({
"epoch": 0,
"mounts": ["/vfs/artifacts", "/vfs/sources", "/vfs/blobs"],
"large_bytes_embedded": false,
}),
)?;
write_json(&debug_path, &json!(inspection.metadata.debug_metadata))?;
let manifest = json!({
"kind": "clusterflux-bundle",
"format_version": 1,
"package": wasm.package,
"target": wasm.target,
"bundle_digest": bundle_digest,
"module": "module.wasm",
"module_size_bytes": wasm.bytes.len(),
"task_descriptors": "task-descriptors.json",
"entrypoints": "entrypoints.json",
"environments": "environments.json",
"source_provider": "source-provider.json",
"vfs_seed": "vfs-seed.json",
"debug_metadata": "debug-metadata.json",
"required_capabilities": tasks.iter().flat_map(|task| {
task["required_capabilities"].as_array().into_iter().flatten().cloned()
}).collect::<Vec<_>>(),
"metadata_identity": inspection.metadata.identity,
"coordinator_receives_source_bytes_by_default": false,
"embeds_full_repository": false,
});
write_json(&manifest_path, &manifest)?;
Ok(json!({
"directory": output,
"manifest": manifest_path,
"module": module_path,
"compiled_module_source": wasm.source_path,
"bundle_digest": bundle_digest,
"module_size_bytes": wasm.bytes.len(),
"task_descriptor_count": tasks.len(),
"entrypoint_count": entrypoints.len(),
"files": [
"manifest.json",
"module.wasm",
"task-descriptors.json",
"entrypoints.json",
"environments.json",
"source-provider.json",
"vfs-seed.json",
"debug-metadata.json",
],
}))
}
fn write_json(path: &Path, value: &Value) -> Result<()> {
let mut bytes = serde_json::to_vec_pretty(value)?;
bytes.push(b'\n');
std::fs::write(path, bytes).with_context(|| format!("failed to write {}", path.display()))
}