@@ -8,6 +8,7 @@ use anyhow::{bail, Context, Result};
88use clap:: { CommandFactory , Parser } ;
99use spin_loader:: bindle:: BindleConnectionInfo ;
1010use spin_manifest:: ApplicationTrigger ;
11+ use spin_trigger:: cli:: { SPIN_LOCKED_URL , SPIN_WORKING_DIR } ;
1112use tempfile:: TempDir ;
1213
1314use crate :: opts:: * ;
@@ -77,21 +78,23 @@ pub struct UpCommand {
7778 ) ]
7879 pub insecure : bool ,
7980
81+ /// Pass an environment variable (key=value) to all components of the application.
82+ #[ clap( short = 'e' , long = "env" , parse( try_from_str = parse_env_var) ) ]
83+ pub env : Vec < ( String , String ) > ,
84+
8085 /// Temporary directory for the static assets of the components.
8186 #[ clap( long = "temp" ) ]
8287 pub tmp : Option < PathBuf > ,
8388
84- /// Set the static assets of the components in the temporary directory as writable.
85- #[ clap( long = "allow-transient-write" ) ]
86- pub allow_transient_write : bool ,
87-
8889 /// All other args, to be passed through to the trigger
8990 #[ clap( hide = true ) ]
9091 pub trigger_args : Vec < OsString > ,
9192}
9293
9394impl UpCommand {
9495 pub async fn run ( self ) -> Result < ( ) > {
96+ // For displaying help, first print `spin up`'s own usage text, then
97+ // attempt to load an app and print trigger-type-specific usage.
9598 let help = self . help ;
9699 if help {
97100 Self :: command ( )
@@ -117,49 +120,43 @@ impl UpCommand {
117120 } ;
118121 let working_dir = working_dir_holder. path ( ) ;
119122
120- let app = match ( & self . app , & self . bindle ) {
123+ let mut app = match ( & self . app , & self . bindle ) {
121124 ( app, None ) => {
122125 let manifest_file = app
123126 . as_deref ( )
124127 . unwrap_or_else ( || DEFAULT_MANIFEST_FILE . as_ref ( ) ) ;
125128 let bindle_connection = self . bindle_connection ( ) ;
126- spin_loader:: from_file (
127- manifest_file,
128- working_dir,
129- & bindle_connection,
130- self . allow_transient_write ,
131- )
132- . await ?
129+ spin_loader:: from_file ( manifest_file, working_dir, & bindle_connection) . await ?
133130 }
134131 ( None , Some ( bindle) ) => match & self . server {
135- Some ( server) => {
136- spin_loader:: from_bindle (
137- bindle,
138- server,
139- working_dir,
140- self . allow_transient_write ,
141- )
142- . await ?
143- }
132+ Some ( server) => spin_loader:: from_bindle ( bindle, server, working_dir) . await ?,
144133 _ => bail ! ( "Loading from a bindle requires a Bindle server URL" ) ,
145134 } ,
146135 ( Some ( _) , Some ( _) ) => bail ! ( "Specify only one of app file or bindle ID" ) ,
147136 } ;
148137
149- let manifest_url = match app. info . origin {
150- spin_manifest:: ApplicationOrigin :: File ( path) => {
151- format ! ( "file:{}" , path. canonicalize( ) ?. to_string_lossy( ) )
152- }
153- spin_manifest:: ApplicationOrigin :: Bindle { id, server } => {
154- format ! ( "bindle+{}?id={}" , server, id)
138+ // Apply --env to component environments
139+ if !self . env . is_empty ( ) {
140+ for component in app. components . iter_mut ( ) {
141+ component. wasm . environment . extend ( self . env . iter ( ) . cloned ( ) ) ;
155142 }
156- } ;
143+ }
157144
158145 let trigger_type = match app. info . trigger {
159146 ApplicationTrigger :: Http ( _) => "http" ,
160147 ApplicationTrigger :: Redis ( _) => "redis" ,
161148 } ;
162149
150+ // Build and write app lock file
151+ let locked_app = spin_trigger:: locked:: build_locked_app ( app, working_dir) ?;
152+ let locked_path = working_dir. join ( "spin.lock" ) ;
153+ let locked_app_contents =
154+ serde_json:: to_vec_pretty ( & locked_app) . context ( "failed to serialize locked app" ) ?;
155+ std:: fs:: write ( & locked_path, locked_app_contents)
156+ . with_context ( || format ! ( "failed to write {:?}" , locked_path) ) ?;
157+ let locked_url = format ! ( "file://{}" , locked_path. to_string_lossy( ) ) ;
158+
159+ // For `spin up --help`, we just want the executor to dump its own argument usage info
163160 let trigger_args = if self . help {
164161 vec ! [ OsString :: from( "--help-args-only" ) ]
165162 } else {
@@ -170,24 +167,16 @@ impl UpCommand {
170167 // via hard-link. I think it should be fine as long as we aren't `setuid`ing this binary.
171168 let mut cmd = std:: process:: Command :: new ( std:: env:: current_exe ( ) . unwrap ( ) ) ;
172169 cmd. arg ( "trigger" )
173- . env ( "SPIN_WORKING_DIR" , working_dir)
174- . env ( "SPIN_MANIFEST_URL" , manifest_url)
175- . env ( "SPIN_TRIGGER_TYPE" , trigger_type)
176- . env (
177- "SPIN_ALLOW_TRANSIENT_WRITE" ,
178- self . allow_transient_write . to_string ( ) ,
179- )
170+ . env ( SPIN_WORKING_DIR , working_dir)
171+ . env ( SPIN_LOCKED_URL , locked_url)
180172 . arg ( trigger_type)
181173 . args ( trigger_args) ;
182174
183- if let Some ( bindle_server) = self . server {
184- cmd. env ( BINDLE_URL_ENV , bindle_server) ;
185- }
186-
187175 tracing:: trace!( "Running trigger executor: {:?}" , cmd) ;
188176
189177 let mut child = cmd. spawn ( ) . context ( "Failed to execute trigger" ) ?;
190178
179+ // Terminate trigger executor if `spin up` itself receives a termination signal
191180 #[ cfg( not( windows) ) ]
192181 {
193182 // https://github.com/nix-rust/nix/issues/656
@@ -232,3 +221,12 @@ impl WorkingDirectory {
232221 }
233222 }
234223}
224+
225+ // Parse the environment variables passed in `key=value` pairs.
226+ fn parse_env_var ( s : & str ) -> Result < ( String , String ) > {
227+ let parts: Vec < _ > = s. splitn ( 2 , '=' ) . collect ( ) ;
228+ if parts. len ( ) != 2 {
229+ bail ! ( "Environment variable must be of the form `key=value`" ) ;
230+ }
231+ Ok ( ( parts[ 0 ] . to_owned ( ) , parts[ 1 ] . to_owned ( ) ) )
232+ }
0 commit comments