Skip to main content

staged_release/
staged_release.rs

1//! Run the shared Monty release recipe against an owned disposable fixture.
2
3use std::error::Error;
4use std::fs;
5use std::path::PathBuf;
6use std::time::{SystemTime, UNIX_EPOCH};
7
8use vsh::{PrincipalId, ReceiptDetail, RunRequest, Runtime, RuntimeConfig, RuntimeDecision};
9
10struct Workspace(PathBuf);
11
12impl Workspace {
13    fn new() -> Result<Self, Box<dyn Error>> {
14        let nonce = SystemTime::now().duration_since(UNIX_EPOCH)?.as_nanos();
15        let path =
16            std::env::temp_dir().join(format!("vsh-cookbook-{}-{nonce}", std::process::id()));
17        fs::create_dir(&path)?;
18        Ok(Self(path))
19    }
20}
21
22impl Drop for Workspace {
23    fn drop(&mut self) {
24        // Only the unique directory successfully created by this guard is owned.
25        let _ = fs::remove_dir_all(&self.0);
26    }
27}
28
29fn main() -> Result<(), Box<dyn Error>> {
30    let workspace = Workspace::new()?;
31    fs::create_dir(workspace.0.join("templates"))?;
32    fs::write(
33        workspace.0.join("templates/service.toml"),
34        b"channel = \"dev\"\n",
35    )?;
36    let runtime = Runtime::open(RuntimeConfig::new(&workspace.0))?;
37    let code = include_str!("staged_release.monty");
38    let preview = runtime.preview(RunRequest::new(code).with_detail(ReceiptDetail::Full))?;
39    assert!(matches!(
40        preview.decision,
41        RuntimeDecision::PendingApproval(_)
42    ));
43    assert_eq!(preview.changed_paths, 3);
44    assert!(!workspace.0.join("release").exists());
45    assert_eq!(
46        preview
47            .changes
48            .iter()
49            .map(|entry| entry.path.as_str())
50            .collect::<Vec<_>>(),
51        ["release", "release/README.txt", "release/app.toml"]
52    );
53    let now = u64::try_from(SystemTime::now().duration_since(UNIX_EPOCH)?.as_millis())?;
54    // A rename is a semantic risk even if it only rearranges generated files.
55    // Production code must authenticate a reviewer before this trusted call.
56    runtime.approve(
57        preview.transaction,
58        PrincipalId::digest_label("fixture-reviewer"),
59        now,
60        now + 30_000,
61    )?;
62    let committed = runtime.commit(preview.transaction, now)?;
63    assert_eq!(committed.transaction, preview.transaction);
64    assert!(committed.commit.is_some());
65    assert_eq!(
66        fs::read_to_string(workspace.0.join("release/app.toml"))?,
67        "channel = \"stable\"\n"
68    );
69    assert_eq!(
70        fs::read_to_string(workspace.0.join("release/README.txt"))?,
71        "channel=stable\n"
72    );
73    assert!(!workspace.0.join("release/service.toml").exists());
74    println!(
75        "Committed {} reviewed paths: {}",
76        committed.changed_paths, committed.transaction
77    );
78    Ok(())
79}