@@ -2,21 +2,11 @@ use core::fmt;
2
2
use std:: {
3
3
collections:: HashMap ,
4
4
fmt:: Display ,
5
- fs:: File ,
6
- io:: BufRead ,
7
5
path:: { Path , PathBuf } ,
8
6
} ;
9
7
10
8
use error_stack:: { Context , Result , ResultExt } ;
11
9
12
- use ignore:: WalkBuilder ;
13
- use rayon:: prelude:: { IntoParallelIterator , ParallelIterator } ;
14
- use regex:: Regex ;
15
- use tracing:: { info, instrument} ;
16
-
17
- use crate :: config:: Config ;
18
- use glob_match:: glob_match;
19
-
20
10
pub struct Project {
21
11
pub base_path : PathBuf ,
22
12
pub files : Vec < ProjectFile > ,
@@ -33,7 +23,7 @@ pub struct VendoredGem {
33
23
pub name : String ,
34
24
}
35
25
36
- #[ derive( Debug ) ]
26
+ #[ derive( Debug , Clone ) ]
37
27
pub struct ProjectFile {
38
28
pub owner : Option < String > ,
39
29
pub path : PathBuf ,
@@ -49,6 +39,7 @@ pub struct Team {
49
39
pub avoid_ownership : bool ,
50
40
}
51
41
42
+ #[ derive( Clone , Debug ) ]
52
43
pub struct Package {
53
44
pub path : PathBuf ,
54
45
pub package_type : PackageType ,
@@ -73,7 +64,7 @@ impl DirectoryCodeownersFile {
73
64
}
74
65
}
75
66
76
- #[ derive( PartialEq , Eq , Debug ) ]
67
+ #[ derive( PartialEq , Eq , Debug , Clone ) ]
77
68
pub enum PackageType {
78
69
Ruby ,
79
70
Javascript ,
@@ -85,7 +76,7 @@ impl Display for PackageType {
85
76
}
86
77
}
87
78
88
- mod deserializers {
79
+ pub mod deserializers {
89
80
use serde:: Deserialize ;
90
81
91
82
#[ derive( Deserialize ) ]
@@ -155,113 +146,6 @@ impl fmt::Display for Error {
155
146
impl Context for Error { }
156
147
157
148
impl Project {
158
- #[ instrument( level = "debug" , skip_all) ]
159
- pub fn build ( base_path : & Path , codeowners_file_path : & Path , config : & Config ) -> Result < Self , Error > {
160
- info ! ( base_path = base_path. to_str( ) , "scanning project" ) ;
161
-
162
- let mut owned_file_paths: Vec < PathBuf > = Vec :: new ( ) ;
163
- let mut packages: Vec < Package > = Vec :: new ( ) ;
164
- let mut teams: Vec < Team > = Vec :: new ( ) ;
165
- let mut vendored_gems: Vec < VendoredGem > = Vec :: new ( ) ;
166
- let mut directory_codeowner_files: Vec < DirectoryCodeownersFile > = Vec :: new ( ) ;
167
-
168
- let mut builder = WalkBuilder :: new ( base_path) ;
169
- builder. hidden ( false ) ;
170
- let walkdir = builder. build ( ) ;
171
-
172
- for entry in walkdir {
173
- let entry = entry. change_context ( Error :: Io ) ?;
174
-
175
- let absolute_path = entry. path ( ) ;
176
- let relative_path = absolute_path. strip_prefix ( base_path) . change_context ( Error :: Io ) ?. to_owned ( ) ;
177
-
178
- if entry. file_type ( ) . unwrap ( ) . is_dir ( ) {
179
- if relative_path. parent ( ) == Some ( Path :: new ( & config. vendored_gems_path ) ) {
180
- let file_name = relative_path. file_name ( ) . expect ( "expected a file_name" ) ;
181
- vendored_gems. push ( VendoredGem {
182
- path : absolute_path. to_owned ( ) ,
183
- name : file_name. to_string_lossy ( ) . to_string ( ) ,
184
- } )
185
- }
186
-
187
- continue ;
188
- }
189
-
190
- let file_name = relative_path. file_name ( ) . expect ( "expected a file_name" ) ;
191
-
192
- if file_name. eq_ignore_ascii_case ( "package.yml" ) && matches_globs ( relative_path. parent ( ) . unwrap ( ) , & config. ruby_package_paths ) {
193
- if let Some ( owner) = ruby_package_owner ( absolute_path) ? {
194
- packages. push ( Package {
195
- path : relative_path. clone ( ) ,
196
- owner,
197
- package_type : PackageType :: Ruby ,
198
- } )
199
- }
200
- }
201
-
202
- if file_name. eq_ignore_ascii_case ( "package.json" )
203
- && matches_globs ( relative_path. parent ( ) . unwrap ( ) , & config. javascript_package_paths )
204
- {
205
- if let Some ( owner) = javascript_package_owner ( absolute_path) ? {
206
- packages. push ( Package {
207
- path : relative_path. clone ( ) ,
208
- owner,
209
- package_type : PackageType :: Javascript ,
210
- } )
211
- }
212
- }
213
-
214
- if file_name. eq_ignore_ascii_case ( ".codeowner" ) {
215
- let owner = std:: fs:: read_to_string ( absolute_path) . change_context ( Error :: Io ) ?;
216
- let owner = owner. trim ( ) . to_owned ( ) ;
217
-
218
- let relative_path = relative_path. to_owned ( ) ;
219
- directory_codeowner_files. push ( DirectoryCodeownersFile {
220
- path : relative_path,
221
- owner,
222
- } )
223
- }
224
-
225
- if matches_globs ( & relative_path, & config. team_file_glob ) {
226
- let file = File :: open ( absolute_path) . change_context ( Error :: Io ) ?;
227
- let deserializer: deserializers:: Team = serde_yaml:: from_reader ( file) . change_context ( Error :: SerdeYaml ) ?;
228
-
229
- teams. push ( Team {
230
- path : absolute_path. to_owned ( ) ,
231
- name : deserializer. name ,
232
- github_team : deserializer. github . team ,
233
- owned_globs : deserializer. owned_globs ,
234
- owned_gems : deserializer. ruby . map ( |ruby| ruby. owned_gems ) . unwrap_or_default ( ) ,
235
- avoid_ownership : deserializer. github . do_not_add_to_codeowners_file ,
236
- } )
237
- }
238
-
239
- if matches_globs ( & relative_path, & config. owned_globs ) && !matches_globs ( & relative_path, & config. unowned_globs ) {
240
- owned_file_paths. push ( absolute_path. to_owned ( ) )
241
- }
242
- }
243
-
244
- info ! (
245
- owned_file_paths = owned_file_paths. len( ) ,
246
- packages = packages. len( ) ,
247
- teams = teams. len( ) ,
248
- vendored_gems = vendored_gems. len( ) ,
249
- "finished scanning project" ,
250
- ) ;
251
-
252
- let owned_files = owned_files ( owned_file_paths) ;
253
-
254
- Ok ( Project {
255
- base_path : base_path. to_owned ( ) ,
256
- files : owned_files,
257
- vendored_gems,
258
- teams,
259
- packages,
260
- codeowners_file_path : codeowners_file_path. to_path_buf ( ) ,
261
- directory_codeowner_files,
262
- } )
263
- }
264
-
265
149
pub fn get_codeowners_file ( & self ) -> Result < String , Error > {
266
150
let codeowners_file: String = if self . codeowners_file_path . exists ( ) {
267
151
std:: fs:: read_to_string ( & self . codeowners_file_path ) . change_context ( Error :: Io ) ?
@@ -301,78 +185,3 @@ impl Project {
301
185
result
302
186
}
303
187
}
304
-
305
- #[ instrument( level = "debug" , skip_all) ]
306
- fn owned_files ( owned_file_paths : Vec < PathBuf > ) -> Vec < ProjectFile > {
307
- let regexp = Regex :: new ( r#"^(?:#|//) @team (.*)$"# ) . expect ( "error compiling regular expression" ) ;
308
-
309
- info ! ( "opening files to read ownership annotation" ) ;
310
-
311
- owned_file_paths
312
- . into_par_iter ( )
313
- . map ( |path| {
314
- let file = File :: open ( & path) . unwrap_or_else ( |_| panic ! ( "Couldn't open {}" , path. to_string_lossy( ) ) ) ;
315
- let first_line = std:: io:: BufReader :: new ( file) . lines ( ) . next ( ) . transpose ( ) ;
316
- let first_line = first_line. expect ( "error reading first line" ) ;
317
-
318
- if first_line. is_none ( ) {
319
- return ProjectFile { path, owner : None } ;
320
- }
321
-
322
- if let Some ( first_line) = first_line {
323
- let capture = regexp. captures ( & first_line) ;
324
-
325
- if let Some ( capture) = capture {
326
- let first_capture = capture. get ( 1 ) ;
327
-
328
- if let Some ( first_capture) = first_capture {
329
- return ProjectFile {
330
- path,
331
- owner : Some ( first_capture. as_str ( ) . to_string ( ) ) ,
332
- } ;
333
- }
334
- }
335
- }
336
-
337
- ProjectFile { path, owner : None }
338
- } )
339
- . collect ( )
340
- }
341
-
342
- fn ruby_package_owner ( path : & Path ) -> Result < Option < String > , Error > {
343
- let file = File :: open ( path) . change_context ( Error :: Io ) ?;
344
- let deserializer: deserializers:: RubyPackage = serde_yaml:: from_reader ( file) . change_context ( Error :: SerdeYaml ) ?;
345
-
346
- Ok ( deserializer. owner )
347
- }
348
-
349
- fn javascript_package_owner ( path : & Path ) -> Result < Option < String > , Error > {
350
- let file = File :: open ( path) . change_context ( Error :: Io ) ?;
351
- let deserializer: deserializers:: JavascriptPackage = serde_json:: from_reader ( file) . change_context ( Error :: SerdeJson ) ?;
352
-
353
- Ok ( deserializer. metadata . and_then ( |metadata| metadata. owner ) )
354
- }
355
-
356
- fn matches_globs ( path : & Path , globs : & [ String ] ) -> bool {
357
- globs. iter ( ) . any ( |glob| glob_match ( glob, path. to_str ( ) . unwrap ( ) ) )
358
- }
359
-
360
- #[ cfg( test) ]
361
- mod tests {
362
- use super :: * ;
363
-
364
- const OWNED_GLOB : & str = "{app,components,config,frontend,lib,packs,spec,danger,script}/**/*.{rb,arb,erb,rake,js,jsx,ts,tsx}" ;
365
-
366
- #[ test]
367
- fn test_matches_globs ( ) {
368
- // should fail because hidden directories are ignored by glob patterns unless explicitly included
369
- assert ! ( matches_globs( Path :: new( "script/.eslintrc.js" ) , & [ OWNED_GLOB . to_string( ) ] ) ) ;
370
- }
371
-
372
- #[ test]
373
- fn test_glob_match ( ) {
374
- // Exposes bug in glob-match https://github.com/devongovett/glob-match/issues/9
375
- // should fail because hidden directories are ignored by glob patterns unless explicitly included
376
- assert ! ( glob_match( OWNED_GLOB , "script/.eslintrc.js" ) ) ;
377
- }
378
- }
0 commit comments