Skip to main content

canic_host/release_set/
manifest.rs

1use serde::{Deserialize, Serialize};
2use std::{fs, path::Path};
3
4use crate::{
5    durable_io::write_bytes,
6    role_contract::{declared_role_manifest_path, finding_detail},
7};
8
9use super::{
10    build_release_set_entry, config_path, configured_release_roles, load_root_package_version,
11    resolve_artifact_root, root_release_set_manifest_path, workspace_manifest_path,
12};
13
14///
15/// RootReleaseSetManifest
16///
17
18#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
19pub struct RootReleaseSetManifest {
20    pub release_version: String,
21    pub entries: Vec<ReleaseSetEntry>,
22}
23
24///
25/// ReleaseSetEntry
26///
27
28#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
29pub struct ReleaseSetEntry {
30    pub role: String,
31    pub template_id: String,
32    pub artifact_relative_path: String,
33    pub payload_size_bytes: u64,
34    pub payload_sha256_hex: String,
35    pub chunk_size_bytes: u64,
36    pub chunk_sha256_hex: Vec<String>,
37}
38
39// Build and persist the current root release-set manifest from built `.wasm.gz` artifacts.
40pub fn emit_root_release_set_manifest(
41    workspace_root: &Path,
42    icp_root: &Path,
43    network: &str,
44) -> Result<std::path::PathBuf, Box<dyn std::error::Error>> {
45    let config_path = config_path(workspace_root);
46    emit_root_release_set_manifest_with_config(workspace_root, icp_root, network, &config_path)
47}
48
49// Build and persist the current root release-set manifest with an explicit config path.
50pub fn emit_root_release_set_manifest_with_config(
51    workspace_root: &Path,
52    icp_root: &Path,
53    network: &str,
54    config_path: &Path,
55) -> Result<std::path::PathBuf, Box<dyn std::error::Error>> {
56    let artifact_root = resolve_artifact_root(icp_root, network)?;
57    let manifest_path = root_release_set_manifest_path(&artifact_root)?;
58    let root_manifest_path =
59        declared_role_manifest_path(config_path, &canic_core::ids::CanisterRole::ROOT)
60            .map_err(|finding| finding_detail(&finding))?;
61    let release_version = load_root_package_version(
62        &root_manifest_path,
63        &workspace_manifest_path(workspace_root),
64    )?;
65    let entries = configured_release_roles(config_path)?
66        .into_iter()
67        .map(|role_name| build_release_set_entry(icp_root, &artifact_root, &role_name))
68        .collect::<Result<Vec<_>, _>>()?;
69    let manifest = RootReleaseSetManifest {
70        release_version,
71        entries,
72    };
73
74    write_bytes(&manifest_path, &serde_json::to_vec_pretty(&manifest)?)?;
75    Ok(manifest_path)
76}
77
78// Emit the root release-set manifest only once every required ordinary artifact exists.
79pub fn emit_root_release_set_manifest_if_ready(
80    workspace_root: &Path,
81    icp_root: &Path,
82    network: &str,
83) -> Result<Option<std::path::PathBuf>, Box<dyn std::error::Error>> {
84    let artifact_root = resolve_artifact_root(icp_root, network)?;
85    let roles = configured_release_roles(&config_path(workspace_root))?;
86
87    for role_name in roles {
88        let artifact_path = artifact_root
89            .join(&role_name)
90            .join(format!("{role_name}.wasm.gz"));
91        if !artifact_path.is_file() {
92            return Ok(None);
93        }
94    }
95
96    emit_root_release_set_manifest(workspace_root, icp_root, network).map(Some)
97}
98
99// Load one previously emitted root release-set manifest from disk.
100pub fn load_root_release_set_manifest(
101    manifest_path: &Path,
102) -> Result<RootReleaseSetManifest, Box<dyn std::error::Error>> {
103    let source = fs::read(manifest_path)?;
104    Ok(serde_json::from_slice(&source)?)
105}