Skip to main content

cli/apps/
build.rs

1//! CLI adapter for Core-owned App artifact and teardown execution.
2
3use crate::config::Config;
4use anyhow::Result;
5use shine_core::runtime::{
6    AppArtifactAction, AppArtifactPlanRequest, PlanningInputVersions, RuntimeEvent, RuntimeObserver,
7};
8
9pub async fn handle_build(config: &Config, app_id: &str) -> Result<()> {
10    handle_build_approved(config, app_id, true).await
11}
12
13pub async fn handle_unbuild(config: &Config, app_id: &str) -> Result<()> {
14    handle_unbuild_approved(config, app_id, true).await
15}
16
17pub async fn handle_build_approved(config: &Config, app_id: &str, yes: bool) -> Result<()> {
18    run_explicit(config, app_id, AppArtifactAction::Apply, yes).await
19}
20
21pub async fn handle_unbuild_approved(config: &Config, app_id: &str, yes: bool) -> Result<()> {
22    run_explicit(config, app_id, AppArtifactAction::Remove, yes).await
23}
24
25async fn run_explicit(
26    config: &Config,
27    app_id: &str,
28    action: AppArtifactAction,
29    yes: bool,
30) -> Result<()> {
31    let plan_request = AppArtifactPlanRequest {
32        category: app_id.to_string(),
33        action,
34        input_versions: PlanningInputVersions::default(),
35    };
36    let reviewed = crate::lifecycle_plan::review_plans(
37        config,
38        [crate::lifecycle_plan::LifecyclePlanRequest::app_artifact(
39            plan_request.clone(),
40            config,
41        )],
42        yes,
43    )
44    .await?
45    .into_iter()
46    .next()
47    .expect("one reviewed App artifact Plan");
48    let runtime = crate::lifecycle_plan::prepare_runtime(config, &reviewed).await?;
49    let request = reviewed_app_artifact_request(&reviewed.request);
50    runtime
51        .run_app_artifact_approved(request, &reviewed.approval, &mut ExplicitObserver)
52        .await?;
53    Ok(())
54}
55
56fn reviewed_app_artifact_request(
57    request: &crate::lifecycle_plan::LifecyclePlanRequest,
58) -> AppArtifactPlanRequest {
59    match request {
60        crate::lifecycle_plan::LifecyclePlanRequest::AppArtifact(request) => request.clone(),
61        _ => unreachable!("reviewed App artifact Plan must retain its artifact request"),
62    }
63}
64
65struct ExplicitObserver;
66
67impl RuntimeObserver for ExplicitObserver {
68    fn emit(&mut self, _event: RuntimeEvent) {}
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74    use shine_core::runtime::OpaqueSecretVersion;
75
76    #[test]
77    fn artifact_execution_reuses_the_reviewed_input_versions() {
78        let mut input_versions = PlanningInputVersions::default();
79        input_versions.insert_secret_version(
80            "CLASH_CONTROLLER_TOKEN",
81            OpaqueSecretVersion::new("test-version"),
82        );
83        let request =
84            crate::lifecycle_plan::LifecyclePlanRequest::AppArtifact(AppArtifactPlanRequest {
85                category: "clash-verge".to_string(),
86                action: AppArtifactAction::Apply,
87                input_versions: input_versions.clone(),
88            });
89
90        assert_eq!(
91            reviewed_app_artifact_request(&request).input_versions,
92            input_versions
93        );
94    }
95}