@@ -4,7 +4,7 @@ use crate::{
4
4
tests:: config:: BOOTSTRAP_SCRIPT ,
5
5
} ;
6
6
use anyhow:: { bail, Context , Result } ;
7
- use ssh2:: Session ;
7
+ use ssh2:: { File , Session } ;
8
8
use std:: {
9
9
io:: { self , Read } ,
10
10
net:: { IpAddr , SocketAddr , TcpStream } ,
@@ -115,27 +115,27 @@ fn blocking_ssh(
115
115
let temp_dir = Path :: new ( remote_temp_dir) ;
116
116
// Transfer a test runner
117
117
let source = local_runner_dir. join ( "test-runner" ) ;
118
- ssh_send_file ( & session, & source, temp_dir)
118
+ ssh_send_file_with_opts ( & session, & source, temp_dir, FileOpts { executable : true } )
119
119
. with_context ( || format ! ( "Failed to send '{source:?}' to remote" ) ) ?;
120
120
121
121
// Transfer connection-checker
122
122
let source = local_runner_dir. join ( "connection-checker" ) ;
123
- ssh_send_file ( & session, & source, temp_dir)
123
+ ssh_send_file_with_opts ( & session, & source, temp_dir, FileOpts { executable : true } )
124
124
. with_context ( || format ! ( "Failed to send '{source:?}' to remote" ) ) ?;
125
125
126
126
// Transfer app packages
127
127
let source = & local_app_manifest. app_package_path ;
128
- ssh_send_file ( & session, source, temp_dir)
128
+ ssh_send_file_with_opts ( & session, source, temp_dir, FileOpts { executable : true } )
129
129
. with_context ( || format ! ( "Failed to send '{source:?}' to remote" ) ) ?;
130
130
131
131
if let Some ( source) = & local_app_manifest. app_package_to_upgrade_from_path {
132
- ssh_send_file ( & session, source, temp_dir)
132
+ ssh_send_file_with_opts ( & session, source, temp_dir, FileOpts { executable : true } )
133
133
. with_context ( || format ! ( "Failed to send '{source:?}' to remote" ) ) ?;
134
134
} else {
135
135
log:: warn!( "No previous app package to upgrade from to send to remote" )
136
136
}
137
137
if let Some ( source) = & local_app_manifest. gui_package_path {
138
- ssh_send_file ( & session, source, temp_dir)
138
+ ssh_send_file_with_opts ( & session, source, temp_dir, FileOpts { executable : true } )
139
139
. with_context ( || format ! ( "Failed to send '{source:?}' to remote" ) ) ?;
140
140
} else {
141
141
log:: warn!( "No UI e2e test to send to remote" )
@@ -182,13 +182,14 @@ fn blocking_ssh(
182
182
Ok ( remote_dir. to_string ( ) )
183
183
}
184
184
185
- /// Copy a `source` file to `dest_dir` in the test runner.
185
+ /// Copy a `source` file to `dest_dir` in the test runner with opts .
186
186
///
187
- /// Returns the aboslute path in the test runner where the file is stored.
188
- fn ssh_send_file < P : AsRef < Path > + Copy > (
187
+ /// Returns the absolute path in the test runner where the file is stored.
188
+ fn ssh_send_file_with_opts < P : AsRef < Path > + Copy > (
189
189
session : & Session ,
190
190
source : P ,
191
191
dest_dir : & Path ,
192
+ opts : FileOpts ,
192
193
) -> Result < PathBuf > {
193
194
let dest = dest_dir. join (
194
195
source
@@ -206,18 +207,46 @@ fn ssh_send_file<P: AsRef<Path> + Copy>(
206
207
let source = std:: fs:: read ( source)
207
208
. with_context ( || format ! ( "Failed to open file at {}" , source. as_ref( ) . display( ) ) ) ?;
208
209
209
- ssh_write ( session, & dest, & source[ ..] ) ?;
210
+ ssh_write_with_opts ( session, & dest, & source[ ..] , opts ) ?;
210
211
211
212
Ok ( dest)
212
213
}
213
214
214
- /// Analogues to [`std::fs::write`], but over ssh!
215
- fn ssh_write < P : AsRef < Path > > ( session : & Session , dest : P , mut source : impl Read ) -> Result < ( ) > {
215
+ /// Create a new file with opts at location `dest` and write the content of `source` into it.
216
+ /// Returns a handle to the newly created file.
217
+ fn ssh_write_with_opts < P : AsRef < Path > > (
218
+ session : & Session ,
219
+ dest : P ,
220
+ mut source : impl Read ,
221
+ opts : FileOpts ,
222
+ ) -> Result < File > {
216
223
let sftp = session. sftp ( ) ?;
217
224
let mut remote_file = sftp. create ( dest. as_ref ( ) ) ?;
218
225
219
226
io:: copy ( & mut source, & mut remote_file) . context ( "failed to write file" ) ?;
220
227
228
+ if opts. executable {
229
+ make_executable ( & mut remote_file) ?;
230
+ } ;
231
+
232
+ Ok ( remote_file)
233
+ }
234
+
235
+ /// Extra options that may be necessary to configure for files written to the test runner VM.
236
+ /// Used in conjunction with the `ssh_*_with_opts` functions.
237
+ #[ derive( Clone , Copy , Debug , Default ) ]
238
+ struct FileOpts {
239
+ /// If file should be executable.
240
+ executable : bool ,
241
+ }
242
+
243
+ fn make_executable ( file : & mut File ) -> Result < ( ) > {
244
+ // Make sure that the script is executable!
245
+ let mut file_stat = file. stat ( ) ?;
246
+ // 0x111 is the executable bit for Owner/Group/Public
247
+ let perm = file_stat. perm . map ( |perm| perm | 0x111 ) . unwrap_or ( 0x111 ) ;
248
+ file_stat. perm = Some ( perm) ;
249
+ file. setstat ( file_stat) ?;
221
250
Ok ( ( ) )
222
251
}
223
252
0 commit comments