@@ -2,6 +2,7 @@ use std::str::FromStr;
22
33use crate :: agent_type:: definition:: Variables ;
44use crate :: agent_type:: error:: AgentTypeError ;
5+ use crate :: agent_type:: runtime_config:: on_host:: executable:: { Args , Env } ;
56use crate :: agent_type:: runtime_config:: on_host:: package:: rendered:: { Repository , Version } ;
67use crate :: agent_type:: runtime_config:: templateable_value:: TemplateableValue ;
78use crate :: agent_type:: templates:: Templateable ;
@@ -14,6 +15,9 @@ pub mod rendered;
1415pub ( super ) struct Package {
1516 /// Download defines the supported repository sources for the packages.
1617 pub download : Download ,
18+ /// Post-download hook script to execute after downloading and extracting the package.
19+ /// All validations, checks, and installation steps should go here.
20+ pub post_download_hook : Option < PostDownloadHook > ,
1721}
1822
1923pub type PackageID = String ;
@@ -35,11 +39,37 @@ pub struct Oci {
3539 pub public_key_url : Option < TemplateableValue < String > > ,
3640}
3741
42+ #[ derive( Debug , Deserialize , Clone , PartialEq ) ]
43+ pub struct PostDownloadHook {
44+ /// Path to the command/executable to run.
45+ /// Can be an absolute path (e.g., "/bin/bash") or a command name to search in PATH (e.g., "bash").
46+ /// Supports shell interpreters or direct binaries.
47+ pub path : TemplateableValue < String > ,
48+
49+ /// Arguments passed to the command.
50+ /// - When using a shell: first arg is typically the script path, followed by script arguments
51+ /// Example: ["/path/to/install.sh", "--check-dependencies", "--verbose"]
52+ /// - When using a binary directly: arguments for that binary (can be empty)
53+ /// Example: ["--flag", "value"] or []
54+ #[ serde( default ) ]
55+ pub args : Args ,
56+
57+ /// Environmental variables passed to the process.
58+ #[ serde( default ) ]
59+ pub env : Env ,
60+ }
61+
3862impl Templateable for Package {
3963 type Output = rendered:: Package ;
4064 fn template_with ( self , variables : & Variables ) -> Result < Self :: Output , AgentTypeError > {
65+ let post_download_hook = self
66+ . post_download_hook
67+ . map ( |pd| pd. template_with ( variables) )
68+ . transpose ( ) ?;
69+
4170 Ok ( Self :: Output {
4271 download : self . download . template_with ( variables) ?,
72+ post_download_hook,
4373 } )
4474 }
4575}
@@ -86,6 +116,17 @@ impl Templateable for Oci {
86116 }
87117}
88118
119+ impl Templateable for PostDownloadHook {
120+ type Output = rendered:: PostDownloadHook ;
121+ fn template_with ( self , variables : & Variables ) -> Result < Self :: Output , AgentTypeError > {
122+ let path = self . path . template_with ( variables) ?;
123+ let args = self . args . template_with ( variables) ?;
124+ let env = self . env . template_with ( variables) ?;
125+
126+ Ok ( Self :: Output { path, args, env } )
127+ }
128+ }
129+
89130#[ cfg( test) ]
90131mod tests {
91132 use std:: str:: FromStr ;
@@ -140,4 +181,58 @@ mod tests {
140181 assert_eq ! ( rendered_oci. version, Version :: from_str( & version) . unwrap( ) ) ;
141182 assert_eq ! ( rendered_oci. public_key_url, public_key_url) ;
142183 }
184+
185+ #[ test]
186+ fn test_post_download_hook_template_with_variables ( ) {
187+ use std:: collections:: HashMap ;
188+
189+ let mut variables = Variables :: new ( ) ;
190+ variables. insert (
191+ "nr-var:version" . to_string ( ) ,
192+ Variable :: new_final_string_variable ( "1.0.0" . to_string ( ) ) ,
193+ ) ;
194+ variables. insert (
195+ "nr-var:script-path" . to_string ( ) ,
196+ Variable :: new_final_string_variable ( "/opt/install.sh" . to_string ( ) ) ,
197+ ) ;
198+ variables. insert (
199+ "nr-var:env-value" . to_string ( ) ,
200+ Variable :: new_final_string_variable ( "test-value" . to_string ( ) ) ,
201+ ) ;
202+
203+ let mut env_map = HashMap :: new ( ) ;
204+ env_map. insert (
205+ "AGENT_VERSION" . to_string ( ) ,
206+ TemplateableValue :: from_template ( "${nr-var:version}" . to_string ( ) ) ,
207+ ) ;
208+ env_map. insert (
209+ "CUSTOM_VAR" . to_string ( ) ,
210+ TemplateableValue :: from_template ( "${nr-var:env-value}" . to_string ( ) ) ,
211+ ) ;
212+
213+ let post_download_hook = PostDownloadHook {
214+ path : TemplateableValue :: from_template ( "/bin/bash" . to_string ( ) ) ,
215+ args : Args ( vec ! [
216+ TemplateableValue :: from_template( "${nr-var:script-path}" . to_string( ) ) ,
217+ TemplateableValue :: from_template( "--version=${nr-var:version}" . to_string( ) ) ,
218+ ] ) ,
219+ env : Env ( env_map) ,
220+ } ;
221+
222+ let rendered = post_download_hook. template_with ( & variables) . unwrap ( ) ;
223+
224+ assert_eq ! ( rendered. path, "/bin/bash" ) ;
225+ assert_eq ! ( rendered. args. 0 . len( ) , 2 ) ;
226+ assert_eq ! ( rendered. args. 0 [ 0 ] , "/opt/install.sh" ) ;
227+ assert_eq ! ( rendered. args. 0 [ 1 ] , "--version=1.0.0" ) ;
228+ assert_eq ! ( rendered. env. 0 . len( ) , 2 ) ;
229+ assert_eq ! (
230+ rendered. env. 0 . get( "AGENT_VERSION" ) ,
231+ Some ( & "1.0.0" . to_string( ) )
232+ ) ;
233+ assert_eq ! (
234+ rendered. env. 0 . get( "CUSTOM_VAR" ) ,
235+ Some ( & "test-value" . to_string( ) )
236+ ) ;
237+ }
143238}
0 commit comments