1use 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}