@@ -8,6 +8,7 @@ use anyhow::{bail, Context, Result};
8
8
use clap:: { CommandFactory , Parser } ;
9
9
use spin_loader:: bindle:: BindleConnectionInfo ;
10
10
use spin_manifest:: ApplicationTrigger ;
11
+ use spin_trigger:: cli:: { SPIN_LOCKED_URL , SPIN_WORKING_DIR } ;
11
12
use tempfile:: TempDir ;
12
13
13
14
use crate :: opts:: * ;
@@ -77,21 +78,23 @@ pub struct UpCommand {
77
78
) ]
78
79
pub insecure : bool ,
79
80
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
+
80
85
/// Temporary directory for the static assets of the components.
81
86
#[ clap( long = "temp" ) ]
82
87
pub tmp : Option < PathBuf > ,
83
88
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
-
88
89
/// All other args, to be passed through to the trigger
89
90
#[ clap( hide = true ) ]
90
91
pub trigger_args : Vec < OsString > ,
91
92
}
92
93
93
94
impl UpCommand {
94
95
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.
95
98
let help = self . help ;
96
99
if help {
97
100
Self :: command ( )
@@ -117,49 +120,43 @@ impl UpCommand {
117
120
} ;
118
121
let working_dir = working_dir_holder. path ( ) ;
119
122
120
- let app = match ( & self . app , & self . bindle ) {
123
+ let mut app = match ( & self . app , & self . bindle ) {
121
124
( app, None ) => {
122
125
let manifest_file = app
123
126
. as_deref ( )
124
127
. unwrap_or_else ( || DEFAULT_MANIFEST_FILE . as_ref ( ) ) ;
125
128
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 ?
133
130
}
134
131
( 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 ?,
144
133
_ => bail ! ( "Loading from a bindle requires a Bindle server URL" ) ,
145
134
} ,
146
135
( Some ( _) , Some ( _) ) => bail ! ( "Specify only one of app file or bindle ID" ) ,
147
136
} ;
148
137
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 ( ) ) ;
155
142
}
156
- } ;
143
+ }
157
144
158
145
let trigger_type = match app. info . trigger {
159
146
ApplicationTrigger :: Http ( _) => "http" ,
160
147
ApplicationTrigger :: Redis ( _) => "redis" ,
161
148
} ;
162
149
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
163
160
let trigger_args = if self . help {
164
161
vec ! [ OsString :: from( "--help-args-only" ) ]
165
162
} else {
@@ -170,24 +167,16 @@ impl UpCommand {
170
167
// via hard-link. I think it should be fine as long as we aren't `setuid`ing this binary.
171
168
let mut cmd = std:: process:: Command :: new ( std:: env:: current_exe ( ) . unwrap ( ) ) ;
172
169
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)
180
172
. arg ( trigger_type)
181
173
. args ( trigger_args) ;
182
174
183
- if let Some ( bindle_server) = self . server {
184
- cmd. env ( BINDLE_URL_ENV , bindle_server) ;
185
- }
186
-
187
175
tracing:: trace!( "Running trigger executor: {:?}" , cmd) ;
188
176
189
177
let mut child = cmd. spawn ( ) . context ( "Failed to execute trigger" ) ?;
190
178
179
+ // Terminate trigger executor if `spin up` itself receives a termination signal
191
180
#[ cfg( not( windows) ) ]
192
181
{
193
182
// https://github.com/nix-rust/nix/issues/656
@@ -232,3 +221,12 @@ impl WorkingDirectory {
232
221
}
233
222
}
234
223
}
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